WIP Migrate to stimulus.js
This commit is contained in:
Generated
+124
-1
@@ -17,6 +17,7 @@
|
||||
"bourbon": "^4.3.2",
|
||||
"cookieconsent": "^3.1.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"es6-slide-up-down": "^1.0.0",
|
||||
"flatpickr": "^4.6.9",
|
||||
"font-awesome": "^4.7.0",
|
||||
"glightbox": "^3.0.9",
|
||||
@@ -28,11 +29,14 @@
|
||||
"picturefill": "^3.0.3",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"sticky-js": "^1.3.0",
|
||||
"stimulus": "^2.0.0",
|
||||
"stimulus-use": "^0.25.4",
|
||||
"vue": "^2.6.14",
|
||||
"vue-router": "^3.5.2",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-session": "^1.0.0",
|
||||
"vuex": "^3.6.2"
|
||||
"vuex": "^3.6.2",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
|
||||
@@ -1650,6 +1654,32 @@
|
||||
"resolved": "https://registry.npmjs.org/@splidejs/splide/-/splide-2.4.21.tgz",
|
||||
"integrity": "sha512-LdIwSnmNsGybCCnA3QG2dOeRv/rQl46jf7FflXxMGX0ADYWyL6UJHWJbMZvHBi5I67uRJY1ga/W+CvJ39nH26A=="
|
||||
},
|
||||
"node_modules/@stimulus/core": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/core/-/core-2.0.0.tgz",
|
||||
"integrity": "sha512-ff70GafKtzc8zQ1/cG+UvL06GcifPWovf2wBEdjLMh9xO2GOYURO3y2RYgzIGYUIBefQwyfX2CLfJdZFJrEPTw==",
|
||||
"dependencies": {
|
||||
"@stimulus/mutation-observers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stimulus/multimap": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/multimap/-/multimap-2.0.0.tgz",
|
||||
"integrity": "sha512-pMBCewkZCFVB3e5mEMoyO9+9aKzHDITmf3OnPun51YWxlcPdHcwbjqm1ylK63fsoduIE+RowBpFwFqd3poEz4w=="
|
||||
},
|
||||
"node_modules/@stimulus/mutation-observers": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/mutation-observers/-/mutation-observers-2.0.0.tgz",
|
||||
"integrity": "sha512-kx4VAJdPhIGBQKGIoUDC2tupEKorG3A+ckc2b1UiwInKTMAC1axOHU8ebcwhaJIxRqIrs8//4SJo9YAAOx6FEg==",
|
||||
"dependencies": {
|
||||
"@stimulus/multimap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stimulus/webpack-helpers": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-2.0.0.tgz",
|
||||
"integrity": "sha512-D6tJWsAC024MwGEIKlUVYU8Ln87mlrmiwHvYAjipg+s8H4eLxUMQ3PZkWyPevfipH+oR3leuHsjYsK1gN5ViQA=="
|
||||
},
|
||||
"node_modules/@symfony/webpack-encore": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@symfony/webpack-encore/-/webpack-encore-1.5.0.tgz",
|
||||
@@ -4305,6 +4335,11 @@
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-slide-up-down": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-slide-up-down/-/es6-slide-up-down-1.0.0.tgz",
|
||||
"integrity": "sha1-RTBJFkFvRliBBk6Q7t8SHTZmT14="
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
@@ -5242,6 +5277,11 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/hotkeys-js": {
|
||||
"version": "3.8.7",
|
||||
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.8.7.tgz",
|
||||
"integrity": "sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg=="
|
||||
},
|
||||
"node_modules/hpack.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
@@ -9394,6 +9434,26 @@
|
||||
"resolved": "https://registry.npmjs.org/sticky-js/-/sticky-js-1.3.0.tgz",
|
||||
"integrity": "sha512-X5zWjFrw7c2xE4tUtSeK7I+1o252JOyZOlzxsu1Q9qZ6hlk2NCd9Sr/AFxf8hfGkLnE1Pak+iLaDyd883LFRgw=="
|
||||
},
|
||||
"node_modules/stimulus": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stimulus/-/stimulus-2.0.0.tgz",
|
||||
"integrity": "sha512-xipy7BS5TVpg4fX6S8LhrYZp7cmHGjmk09WSAiVx1gF5S5g43IWsuetfUhIk8HfHUG+4MQ9nY0FQz4dRFLs/8w==",
|
||||
"dependencies": {
|
||||
"@stimulus/core": "^2.0.0",
|
||||
"@stimulus/webpack-helpers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stimulus-use": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/stimulus-use/-/stimulus-use-0.25.4.tgz",
|
||||
"integrity": "sha512-Rr4m4PpcOni7y4KiwUMdCuUJ5IPE2LEELwoz8e+VB3jbfPb9DNjrurCTKCWT5BiPBrn9OEfhnWJIRPcbxUpQvQ==",
|
||||
"dependencies": {
|
||||
"hotkeys-js": ">=3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"stimulus": ">=1.1.1 <3"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
@@ -10835,6 +10895,11 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
@@ -12236,6 +12301,32 @@
|
||||
"resolved": "https://registry.npmjs.org/@splidejs/splide/-/splide-2.4.21.tgz",
|
||||
"integrity": "sha512-LdIwSnmNsGybCCnA3QG2dOeRv/rQl46jf7FflXxMGX0ADYWyL6UJHWJbMZvHBi5I67uRJY1ga/W+CvJ39nH26A=="
|
||||
},
|
||||
"@stimulus/core": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/core/-/core-2.0.0.tgz",
|
||||
"integrity": "sha512-ff70GafKtzc8zQ1/cG+UvL06GcifPWovf2wBEdjLMh9xO2GOYURO3y2RYgzIGYUIBefQwyfX2CLfJdZFJrEPTw==",
|
||||
"requires": {
|
||||
"@stimulus/mutation-observers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@stimulus/multimap": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/multimap/-/multimap-2.0.0.tgz",
|
||||
"integrity": "sha512-pMBCewkZCFVB3e5mEMoyO9+9aKzHDITmf3OnPun51YWxlcPdHcwbjqm1ylK63fsoduIE+RowBpFwFqd3poEz4w=="
|
||||
},
|
||||
"@stimulus/mutation-observers": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/mutation-observers/-/mutation-observers-2.0.0.tgz",
|
||||
"integrity": "sha512-kx4VAJdPhIGBQKGIoUDC2tupEKorG3A+ckc2b1UiwInKTMAC1axOHU8ebcwhaJIxRqIrs8//4SJo9YAAOx6FEg==",
|
||||
"requires": {
|
||||
"@stimulus/multimap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@stimulus/webpack-helpers": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-2.0.0.tgz",
|
||||
"integrity": "sha512-D6tJWsAC024MwGEIKlUVYU8Ln87mlrmiwHvYAjipg+s8H4eLxUMQ3PZkWyPevfipH+oR3leuHsjYsK1gN5ViQA=="
|
||||
},
|
||||
"@symfony/webpack-encore": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@symfony/webpack-encore/-/webpack-encore-1.5.0.tgz",
|
||||
@@ -14384,6 +14475,11 @@
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"es6-slide-up-down": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-slide-up-down/-/es6-slide-up-down-1.0.0.tgz",
|
||||
"integrity": "sha1-RTBJFkFvRliBBk6Q7t8SHTZmT14="
|
||||
},
|
||||
"es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
@@ -15116,6 +15212,11 @@
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"hotkeys-js": {
|
||||
"version": "3.8.7",
|
||||
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.8.7.tgz",
|
||||
"integrity": "sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg=="
|
||||
},
|
||||
"hpack.js": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
|
||||
@@ -18251,6 +18352,23 @@
|
||||
"resolved": "https://registry.npmjs.org/sticky-js/-/sticky-js-1.3.0.tgz",
|
||||
"integrity": "sha512-X5zWjFrw7c2xE4tUtSeK7I+1o252JOyZOlzxsu1Q9qZ6hlk2NCd9Sr/AFxf8hfGkLnE1Pak+iLaDyd883LFRgw=="
|
||||
},
|
||||
"stimulus": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stimulus/-/stimulus-2.0.0.tgz",
|
||||
"integrity": "sha512-xipy7BS5TVpg4fX6S8LhrYZp7cmHGjmk09WSAiVx1gF5S5g43IWsuetfUhIk8HfHUG+4MQ9nY0FQz4dRFLs/8w==",
|
||||
"requires": {
|
||||
"@stimulus/core": "^2.0.0",
|
||||
"@stimulus/webpack-helpers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"stimulus-use": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/stimulus-use/-/stimulus-use-0.25.4.tgz",
|
||||
"integrity": "sha512-Rr4m4PpcOni7y4KiwUMdCuUJ5IPE2LEELwoz8e+VB3jbfPb9DNjrurCTKCWT5BiPBrn9OEfhnWJIRPcbxUpQvQ==",
|
||||
"requires": {
|
||||
"hotkeys-js": ">=3"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
@@ -19317,6 +19435,11 @@
|
||||
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
|
||||
+6
-2
@@ -17,6 +17,7 @@
|
||||
"bourbon": "^4.3.2",
|
||||
"cookieconsent": "^3.1.0",
|
||||
"dayjs": "^1.10.6",
|
||||
"es6-slide-up-down": "^1.0.0",
|
||||
"flatpickr": "^4.6.9",
|
||||
"font-awesome": "^4.7.0",
|
||||
"glightbox": "^3.0.9",
|
||||
@@ -28,11 +29,14 @@
|
||||
"picturefill": "^3.0.3",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"sticky-js": "^1.3.0",
|
||||
"stimulus": "^2.0.0",
|
||||
"stimulus-use": "^0.25.4",
|
||||
"vue": "^2.6.14",
|
||||
"vue-router": "^3.5.2",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-session": "^1.0.0",
|
||||
"vuex": "^3.6.2"
|
||||
"vuex": "^3.6.2",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.14.7",
|
||||
@@ -54,8 +58,8 @@
|
||||
"tailwindcss": "^2.2.7",
|
||||
"tailwindcss-alt": "^3.0.0",
|
||||
"tinycolor2": "^1.4.1",
|
||||
"vue-loader": "^15.9.7",
|
||||
"vue-custom-element": "^3.3.0",
|
||||
"vue-loader": "^15.9.7",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
|
||||
* 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\Product;
|
||||
use EP\EpProducts\Domain\Repository\ProductRepository;
|
||||
@@ -85,12 +84,6 @@ class AjaxDateController extends ActionController
|
||||
$this->productRepository = $productRepository;
|
||||
}
|
||||
|
||||
public function initializeAction()
|
||||
{
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$this->processFilterSettingsArgument($forcedConceptUids);
|
||||
}
|
||||
|
||||
public function initializeDatesAction()
|
||||
{
|
||||
if ($this->request->hasArgument('dateFrom')) {
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Service\FilterService;
|
||||
use EP\EpProducts\Service\SearchResultUrlService;
|
||||
use EP\EpProducts\Service\SearchResultService;
|
||||
@@ -66,8 +65,7 @@ class AjaxSearchController extends ActionController
|
||||
FilterService $filterService,
|
||||
SearchResultService $searchResultService,
|
||||
SearchResultUrlService $searchResultUrlService
|
||||
)
|
||||
{
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->filterService = $filterService;
|
||||
@@ -75,34 +73,26 @@ class AjaxSearchController extends ActionController
|
||||
$this->searchResultUrlService = $searchResultUrlService;
|
||||
}
|
||||
|
||||
public function initializeAction()
|
||||
/**
|
||||
* @param array $filterSettings
|
||||
*/
|
||||
public function searchresultAction(array $filterSettings = [])
|
||||
{
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$this->processFilterSettingsArgument($forcedConceptUids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
* @return string
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function searchresultAction(FilterSettings $filterSettings = null)
|
||||
{
|
||||
$dateSelected = false;
|
||||
$organic = true;
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
|
||||
|
||||
if ($filterSettings === null) {
|
||||
$filterSettings = $this->filterService->getDefaultFilterSettings($forcedConceptUids);
|
||||
}
|
||||
|
||||
if ($filterSettings->getDateFrom() !== null || $filterSettings->getDateTo() !== null) {
|
||||
if (isset($filterSettings['dateFrom']) || isset($filterSettings['dateTo'])) {
|
||||
$dateSelected = true;
|
||||
}
|
||||
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$searchResult = $this
|
||||
->searchResultService
|
||||
->getResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
if ($searchResult->getTotal() === 0) {
|
||||
$filterSettings = $this->filterService->getDefaultFilterSettings($forcedConceptUids);
|
||||
@@ -110,11 +100,7 @@ class AjaxSearchController extends ActionController
|
||||
$organic = false;
|
||||
}
|
||||
|
||||
$options = [
|
||||
'defaultDetailPageUid' => $this->settings['defaultDetailPageUid'],
|
||||
];
|
||||
|
||||
$this->searchResultUrlService->process($searchResult, $options);
|
||||
$this->searchResultUrlService->process($searchResult, $this->settings['defaultDetailPageUid']);
|
||||
|
||||
$filterOptions = $this->filterService->getFilterOptions(
|
||||
$filterSettings,
|
||||
@@ -129,7 +115,7 @@ class AjaxSearchController extends ActionController
|
||||
->setTargetPageUid($this->settings['defaultSearchPageUid'])
|
||||
->uriFor(
|
||||
'searchresult',
|
||||
['filterSettings' => $filterSettings->toArray()],
|
||||
['filterSettings' => $filterSettings],
|
||||
'Search',
|
||||
null,
|
||||
'searchresult'
|
||||
@@ -144,7 +130,7 @@ class AjaxSearchController extends ActionController
|
||||
'meta' => [
|
||||
'organic' => $organic,
|
||||
'total' => $searchResult->getTotal(),
|
||||
'dateRange' => DateUtility::formatDateRange($filterSettings->getDateFrom(), $filterSettings->getDateTo()),
|
||||
'dateRange' => DateUtility::formatDateRange($filterSettings['dateFrom'], $filterSettings['dateTo']),
|
||||
'dateSelected' => $dateSelected,
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Model\SearchParams;
|
||||
use EP\EpProducts\Service\FilterService;
|
||||
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
||||
@@ -70,7 +69,7 @@ class AjaxSearchbarController extends ActionController
|
||||
$searchParams = new SearchParams();
|
||||
$filterSettings = $this->filterService->getDefaultFilterSettings($forcedConceptUids);
|
||||
} else {
|
||||
$filterSettings = FilterSettings::fromSearchParams($searchParams);
|
||||
$filterSettings = $searchParams->toFilterSettings();
|
||||
}
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace EP\EpProducts\Controller;
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\Concept;
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Repository\ConceptRepository;
|
||||
use EP\EpProducts\Service\ProductDataService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
@@ -69,11 +68,13 @@ class ConceptController extends ActionController
|
||||
}
|
||||
$teaserConceptUids = GeneralUtility::trimExplode(',', $this->settings['additionalConceptUids']);
|
||||
array_unshift($teaserConceptUids, $concept->getUid());
|
||||
$filterSettings = new FilterSettings();
|
||||
$filterSettings->setConceptUids($teaserConceptUids);
|
||||
$filterSettings = [
|
||||
'conceptUids' => $teaserConceptUids,
|
||||
];
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
|
||||
$limit = $this->settings['showMoreLink'] ? 4 : 0;
|
||||
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
|
||||
|
||||
$this->view->assignMultiple([
|
||||
'teasers' => $teasers,
|
||||
'concept' => $concept,
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace EP\EpProducts\Controller;
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\Country;
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Repository\CountryRepository;
|
||||
use EP\EpProducts\Service\ProductDataService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
@@ -67,8 +66,9 @@ class CountryController extends ActionController
|
||||
$countryUid = $this->settings['countryUid'];
|
||||
$country = $this->countryRepository->findByIdentifier($countryUid);
|
||||
}
|
||||
$filterSettings = new FilterSettings();
|
||||
$filterSettings->setCountryUids([$countryUid]);
|
||||
$filterSettings = [
|
||||
'countryUids' => [ $countryUid ],
|
||||
];
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
|
||||
$limit = $this->settings['showMoreLink'] ? 4 : 0;
|
||||
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
|
||||
* 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\Repository\HotelRepository;
|
||||
use EP\EpProducts\Domain\Repository\ProductRepository;
|
||||
@@ -92,8 +91,9 @@ class HotelController extends ActionController
|
||||
/** @var Hotel $hotel */
|
||||
$hotel = $this->hotelRepository->findByIdentifier($hotelUid);
|
||||
}
|
||||
$filterSettings = new FilterSettings();
|
||||
$filterSettings->setHotelUid($hotelUid);
|
||||
$filterSettings = [
|
||||
'hotelUid' => $hotelUid,
|
||||
];
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
|
||||
$limit = $this->settings['showMoreLink'] ? 4 : 0;
|
||||
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
|
||||
|
||||
@@ -122,7 +122,7 @@ class ProductController extends ActionController
|
||||
'searchresult',
|
||||
'Search',
|
||||
null,
|
||||
['filterSettings' => $filterSettings->toArray()],
|
||||
['filterSettings' => $filterSettings],
|
||||
$this->settings['defaultSearchPageUid']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Model\Region;
|
||||
use EP\EpProducts\Domain\Repository\RegionRepository;
|
||||
use EP\EpProducts\Service\ProductDataService;
|
||||
@@ -72,8 +71,9 @@ class RegionController extends ActionController
|
||||
return (int)$uid;
|
||||
}, $additionalRegionUids);
|
||||
$teaserRegionUids = array_merge([$region->getUid()], $additionalRegionUids);
|
||||
$filterSettings = new FilterSettings();
|
||||
$filterSettings->setRegionUids($teaserRegionUids);
|
||||
$filterSettings = [
|
||||
'regionUids' => $teaserRegionUids,
|
||||
];
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
|
||||
$limit = $this->settings['showMoreLink'] ? 4 : 0;
|
||||
$teasers = $this->productDataService->getTeasergroup($filterSettings, $excludedConceptUids, $limit);
|
||||
|
||||
@@ -27,11 +27,13 @@ namespace EP\EpProducts\Controller;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Model\SearchParams;
|
||||
use EP\EpProducts\Service\FilterService;
|
||||
use EP\EpProducts\Service\FilterSettingsEncoder;
|
||||
use EP\EpProducts\Service\SearchResultService;
|
||||
use EP\EpProducts\Service\SearchResultUrlService;
|
||||
use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait;
|
||||
use EP\EpProducts\Utility\DateUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||
@@ -47,7 +49,17 @@ class SearchController extends ActionController
|
||||
protected $filterService;
|
||||
|
||||
/**
|
||||
* @var \EP\EpProducts\Domain\Model\FilterSettings
|
||||
* @var SearchResultService
|
||||
*/
|
||||
private $searchResultService;
|
||||
|
||||
/**
|
||||
* @var SearchResultUrlService
|
||||
*/
|
||||
private $searchResultUrlService;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultFilterSettings;
|
||||
|
||||
@@ -59,10 +71,16 @@ class SearchController extends ActionController
|
||||
/**
|
||||
* @param FilterService $filterService
|
||||
*/
|
||||
public function __construct(FilterService $filterService)
|
||||
{
|
||||
public function __construct
|
||||
(
|
||||
FilterService $filterService,
|
||||
SearchResultService $searchResultService,
|
||||
SearchResultUrlService $searchResultUrlService
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->filterService = $filterService;
|
||||
$this->searchResultService = $searchResultService;
|
||||
$this->searchResultUrlService = $searchResultUrlService;
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids'], true);
|
||||
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids'], true);
|
||||
@@ -104,19 +122,17 @@ class SearchController extends ActionController
|
||||
|
||||
/**
|
||||
* @param SearchParams $searchParams
|
||||
* @throws StopActionException
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
|
||||
*/
|
||||
public function processSearchAction(SearchParams $searchParams)
|
||||
{
|
||||
$filterSettings = FilterSettings::fromSearchParams($searchParams);
|
||||
$filterSettings = $searchParams->toFilterSettings();
|
||||
|
||||
$redirectUri = $this->uriBuilder
|
||||
->reset()
|
||||
->setCreateAbsoluteUri(true)
|
||||
->uriFor(
|
||||
'searchresult',
|
||||
['filterSettings' => $filterSettings->toArray()],
|
||||
['filterSettings' => $filterSettings],
|
||||
null,
|
||||
null,
|
||||
'searchresult'
|
||||
@@ -128,17 +144,33 @@ class SearchController extends ActionController
|
||||
public function initializeSearchresultAction()
|
||||
{
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$this->processFilterSettingsArgument($forcedConceptUids);
|
||||
//$this->processFilterSettingsArgument($forcedConceptUids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \EP\EpProducts\Domain\Model\FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
*/
|
||||
public function searchresultAction(FilterSettings $filterSettings)
|
||||
public function searchresultAction(array $filterSettings = [])
|
||||
{
|
||||
$dateSelected = false;
|
||||
$organic = true;
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
$excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']);
|
||||
$excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']);
|
||||
$forcedConceptUids = GeneralUtility::trimExplode(',', $this->settings['forcedConceptUids'], true);
|
||||
|
||||
if (!empty($filterSettings['dateFrom']) || !empty($filterSettings['dateTo'])) {
|
||||
$dateSelected = true;
|
||||
}
|
||||
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
if ($searchResult->getTotal() === 0) {
|
||||
$filterSettings = $this->defaultFilterSettings;
|
||||
$searchResult = $this->searchResultService->getResult($filterSettings, $excludedConceptUids);
|
||||
$organic = false;
|
||||
}
|
||||
|
||||
$this->searchResultUrlService->process($searchResult, $this->settings['defaultDetailPageUid']);
|
||||
|
||||
$filterOptions = $this->filterService->getFilterOptions(
|
||||
$filterSettings,
|
||||
@@ -147,10 +179,30 @@ class SearchController extends ActionController
|
||||
$forcedConceptUids
|
||||
);
|
||||
|
||||
$referringPageUri = $this
|
||||
->uriBuilder
|
||||
->reset()
|
||||
->setTargetPageUid($this->settings['defaultSearchPageUid'])
|
||||
->uriFor(
|
||||
'searchresult',
|
||||
['filterSettings' => $filterSettings],
|
||||
'Search',
|
||||
null,
|
||||
'searchresult'
|
||||
)
|
||||
;
|
||||
|
||||
$this->view->assignMultiple([
|
||||
'referringPageUid' => $this->settings['defaultSearchPageUid'],
|
||||
'filterOptions' => $filterOptions,
|
||||
'filterSettings' => $filterSettings,
|
||||
'filterOptions' => $filterOptions,
|
||||
'searchResult' => $searchResult->getData(),
|
||||
'referringPageUri' => $referringPageUri,
|
||||
'meta' => [
|
||||
'organic' => $organic,
|
||||
'total' => $searchResult->getTotal(),
|
||||
'dateRange' => DateUtility::formatDateRange($filterSettings['dateFrom'], $filterSettings['dateTo']),
|
||||
'dateSelected' => $dateSelected,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $countryUids
|
||||
*/
|
||||
public function setCountryUids(array $countryUids)
|
||||
public function setCountryUids(?array $countryUids)
|
||||
{
|
||||
$this->countryUids = $countryUids;
|
||||
}
|
||||
@@ -278,7 +278,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $regionUids
|
||||
*/
|
||||
public function setRegionUids(array $regionUids)
|
||||
public function setRegionUids(?array $regionUids)
|
||||
{
|
||||
$this->regionUids = $regionUids;
|
||||
}
|
||||
@@ -294,7 +294,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $cityUids
|
||||
*/
|
||||
public function setCityUids($cityUids)
|
||||
public function setCityUids(?array $cityUids)
|
||||
{
|
||||
$this->cityUids = $cityUids;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $conceptUids
|
||||
*/
|
||||
public function setConceptUids($conceptUids)
|
||||
public function setConceptUids(?array $conceptUids)
|
||||
{
|
||||
$this->conceptUids = $conceptUids;
|
||||
}
|
||||
@@ -421,7 +421,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $boardTypes
|
||||
*/
|
||||
public function setBoardTypes($boardTypes)
|
||||
public function setBoardTypes(?array $boardTypes)
|
||||
{
|
||||
$this->boardTypes = $boardTypes;
|
||||
}
|
||||
@@ -437,7 +437,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $hotelTypes
|
||||
*/
|
||||
public function setHotelTypes($hotelTypes)
|
||||
public function setHotelTypes(?array $hotelTypes)
|
||||
{
|
||||
$this->hotelTypes = $hotelTypes;
|
||||
}
|
||||
@@ -453,7 +453,7 @@ class FilterSettings implements \JsonSerializable
|
||||
/**
|
||||
* @param array $roomTypes
|
||||
*/
|
||||
public function setRoomTypes($roomTypes)
|
||||
public function setRoomTypes(?array $roomTypes)
|
||||
{
|
||||
$this->roomTypes = $roomTypes;
|
||||
}
|
||||
|
||||
@@ -235,6 +235,46 @@ class SearchParams implements \JsonSerializable
|
||||
$this->pageUid = $pageUid;
|
||||
}
|
||||
|
||||
public function toFilterSettings(): array
|
||||
{
|
||||
$filterSettings = [];
|
||||
|
||||
if ($this->getDestinationType() && $this->getDestinationUid()) {
|
||||
$destinationUid = $this->getDestinationUid();
|
||||
switch ($this->getDestinationType()) {
|
||||
case SearchParams::DESTINATION_TYPE_COUNTRY:
|
||||
$filterSettings['countryUids'] = [ $destinationUid ];
|
||||
break;
|
||||
case SearchParams::DESTINATION_TYPE_REGION:
|
||||
$filterSettings['regionUids'] = [ $destinationUid ];
|
||||
break;
|
||||
case SearchParams::DESTINATION_TYPE_CITY:
|
||||
$filterSettings['cityUids'] = [ $destinationUid ];
|
||||
break;
|
||||
case SearchParams::DESTINATION_TYPE_CONCEPT:
|
||||
$filterSettings['conceptUids'] = [ $destinationUid ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null !== $this->getDateFrom()) {
|
||||
$filterSettings['dateFrom'] = $this->getDateFrom()->format('Y-m-d');
|
||||
}
|
||||
if (null !== $this->getDateTo()) {
|
||||
$filterSettings['dateTo'] = $this->getDateTo()->format('Y-m-d');
|
||||
}
|
||||
if (null !== $this->getPriceRange()) {
|
||||
$filterSettings['priceRanges'] = [ $this->getPriceRange() ];
|
||||
}
|
||||
if (null !== $this->getPax()) {
|
||||
$filterSettings['pax'] = $this->getPax();
|
||||
}
|
||||
if (null !== $this->getNights()) {
|
||||
$filterSettings['nights'] = $this->getNights();
|
||||
}
|
||||
|
||||
return $filterSettings;
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -40,21 +40,14 @@ class SearchResult
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* @var FilterSettings
|
||||
*/
|
||||
protected $filterSettings;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param int $total
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
public function __construct(array $data = [], $total = 0, FilterSettings $filterSettings = null)
|
||||
public function __construct(array $data = [], $total = 0)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->total = $total;
|
||||
$this->filterSettings = $filterSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,20 +82,4 @@ class SearchResult
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FilterSettings
|
||||
*/
|
||||
public function getFilterSettings()
|
||||
{
|
||||
return $this->filterSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
public function setFilterSettings($filterSettings)
|
||||
{
|
||||
$this->filterSettings = $filterSettings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+53
-113
@@ -42,7 +42,7 @@ abstract class AbstractRepository extends \TYPO3\CMS\Extbase\Persistence\Reposit
|
||||
{
|
||||
/** @var $querySettings \TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings */
|
||||
$querySettings = $this->objectManager->get(Typo3QuerySettings::class);
|
||||
$querySettings->setRespectStoragePage(FALSE);
|
||||
$querySettings->setRespectStoragePage(false);
|
||||
$this->setDefaultQuerySettings($querySettings);
|
||||
}
|
||||
|
||||
@@ -60,114 +60,90 @@ abstract class AbstractRepository extends \TYPO3\CMS\Extbase\Persistence\Reposit
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addDateFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addDateFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Date Start
|
||||
if ($filterSettings->getDateFrom() !== null) {
|
||||
if (!empty($filterSettings['dateFrom'])) {
|
||||
$query
|
||||
->andWhere('date.date_start >= :dateStart')
|
||||
->setParameter('dateStart', $filterSettings->getDateFrom()->format('Y-m-d'))
|
||||
->setParameter('dateStart', $filterSettings['dateFrom'])
|
||||
;
|
||||
}
|
||||
|
||||
// Date End
|
||||
if ($filterSettings->getDateTo() !== null) {
|
||||
if (!empty($filterSettings['dateTo'])) {
|
||||
$query
|
||||
->andWhere('date.date_end <= :dateEnd')
|
||||
->setParameter('dateEnd', $filterSettings->getDateTo()->format('Y-m-d'))
|
||||
->setParameter('dateEnd', $filterSettings['dateTo'])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addHotelFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addHotelFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Hotel
|
||||
if ((int) $filterSettings->getHotelUid() > 0) {
|
||||
if (!empty($filterSettings['hotelUid'])) {
|
||||
$query
|
||||
->andWhere('date.hotel = :hotelUid')
|
||||
->setParameter('hotelUid', $filterSettings->getHotelUid())
|
||||
->setParameter('hotelUid', (int)$filterSettings['hotelUid'])
|
||||
;
|
||||
}
|
||||
// Hotel types
|
||||
if (count($filterSettings->getHotelTypes()) > 0) {
|
||||
if (!empty($filterSettings['hotelTypes'])) {
|
||||
$query
|
||||
->andWhere('date.hotel_type IN (:hotelTypes)')
|
||||
->setParameter('hotelTypes', $filterSettings->getHotelTypes(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('hotelTypes', (array)$filterSettings['hotelTypes'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addCountryRegionAndCityFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addCountryRegionAndCityFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
if (count($filterSettings->getCityUids()) > 0) {
|
||||
if (!empty($filterSettings['cityUids'])) {
|
||||
$query
|
||||
->andWhere('date.city IN (:cityUids)')
|
||||
->setParameter('cityUids', $filterSettings->getCityUids(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('cityUids', (array)$filterSettings['cityUids'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
} elseif (count($filterSettings->getRegionUids()) > 0) {
|
||||
} elseif (!empty($filterSettings['regionUids'])) {
|
||||
$query
|
||||
->andWhere('date.region IN (:regionUids)')
|
||||
->setParameter('regionUids', $filterSettings->getRegionUids(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('regionUids', (array)$filterSettings['regionUids'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
} elseif (count($filterSettings->getCountryUids()) > 0) {
|
||||
} elseif (!empty($filterSettings['countryUids'])) {
|
||||
$query
|
||||
->andWhere('date.country IN (:countryUids)')
|
||||
->setParameter('countryUids', $filterSettings->getCountryUids(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('countryUids', (array)$filterSettings['countryUids'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addConceptFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addConceptFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
if (count($filterSettings->getConceptUids()) > 0) {
|
||||
if (!empty($filterSettings['conceptUids'])) {
|
||||
$query
|
||||
->andWhere('date.concept IN (:conceptUids)')
|
||||
->setParameter('conceptUids', $filterSettings->getConceptUids(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('conceptUids', (array)$filterSettings['conceptUids'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addPaxFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addPaxFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Pax
|
||||
if ((int) $filterSettings->getPax() > 0) {
|
||||
if (!empty($filterSettings['pax'])) {
|
||||
$query
|
||||
->andWhere('date.available >= :pax')
|
||||
->setParameter('pax', $filterSettings->getPax())
|
||||
->setParameter('pax', (int)$filterSettings['pax'])
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addPriceRangeFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addPriceRangeFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Price ranges
|
||||
if (count($filterSettings->getPriceRanges()) > 0) {
|
||||
if (!empty($filterSettings['priceRanges'])) {
|
||||
$orX = $query->expr()->orX();
|
||||
foreach ($filterSettings->getPriceRanges() as $priceRange) {
|
||||
foreach ((array)$filterSettings['priceRanges'] as $priceRange) {
|
||||
[ $priceMin, $priceMax ] = FilterOptions::$priceRangeOptions[$priceRange];
|
||||
$orX->add($query->expr()->andX(
|
||||
$query->expr()->gte('date.min_price', ':priceMin'),
|
||||
@@ -182,79 +158,63 @@ abstract class AbstractRepository extends \TYPO3\CMS\Extbase\Persistence\Reposit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addDurationFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addDurationFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Duration
|
||||
if ((int) $filterSettings->getNights() === FilterSettings::DURATION_SHORT) {
|
||||
if (empty($filterSettings['nights'])) {
|
||||
return;
|
||||
}
|
||||
$nights = (int)$filterSettings['nights'];
|
||||
if ($nights === FilterSettings::DURATION_SHORT) {
|
||||
$query
|
||||
->andWhere('date.nights <= :nights')
|
||||
->setParameter('nights', $filterSettings->getNights())
|
||||
->setParameter('nights', $nights)
|
||||
;
|
||||
} elseif ((int) $filterSettings->getNights() === FilterSettings::DURATION_LONG) {
|
||||
} elseif ($nights === FilterSettings::DURATION_LONG) {
|
||||
$query
|
||||
->andWhere('date.nights >= :nights')
|
||||
->setParameter('nights', $filterSettings->getNights())
|
||||
->setParameter('nights', $nights)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addHotelTypesFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addHotelTypesFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Hotel types
|
||||
if (count($filterSettings->getHotelTypes()) > 0) {
|
||||
if (!empty($filterSettings['hotelTypes'])) {
|
||||
$query
|
||||
->andWhere('date.hotel_type IN (:hotelTypes)')
|
||||
->setParameter('hotelTypes', $filterSettings->getHotelTypes(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('hotelTypes', (array)$filterSettings['hotelTypes'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addBoardTypesFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addBoardTypesFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Board types
|
||||
if (count($filterSettings->getBoardTypes()) > 0) {
|
||||
if (!empty($filterSettings['boardTypes'])) {
|
||||
$query
|
||||
->andWhere('date.board_bus_pro_id IN (:boardTypes)')
|
||||
->setParameter('boardTypes', $filterSettings->getBoardTypes(), Connection::PARAM_STR_ARRAY)
|
||||
->setParameter('boardTypes', (array)$filterSettings['boardTypes'], Connection::PARAM_STR_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addRoomTypesFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addRoomTypesFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Room types
|
||||
if (count($filterSettings->getRoomTypes()) > 0) {
|
||||
if (!empty($filterSettings['roomTypes'])) {
|
||||
$query
|
||||
->andWhere('room.type IN (:roomTypes)')
|
||||
->setParameter('roomTypes', $filterSettings->getRoomTypes(), Connection::PARAM_INT_ARRAY)
|
||||
->setParameter('roomTypes', (array)$filterSettings['roomTypes'], Connection::PARAM_INT_ARRAY)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
*/
|
||||
protected function addTransportationFilterConditions(QueryBuilder $query, FilterSettings $filterSettings)
|
||||
protected function addTransportationFilterConditions(QueryBuilder $query, array $filterSettings): void
|
||||
{
|
||||
// Transportation
|
||||
if ($filterSettings->getBus() === true) {
|
||||
if (!empty($filterSettings['bus'])) {
|
||||
$query->andWhere('date.bus_available = 1');
|
||||
}
|
||||
}
|
||||
@@ -263,10 +223,10 @@ abstract class AbstractRepository extends \TYPO3\CMS\Extbase\Persistence\Reposit
|
||||
* Adds general conditions for provided filtersettings
|
||||
*
|
||||
* @param QueryBuilder $query
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param bool $constraintDates
|
||||
*/
|
||||
protected function addFiltersettingsConditions(QueryBuilder $query, FilterSettings $filterSettings, $constraintDates = true)
|
||||
protected function addFiltersettingsConditions(QueryBuilder $query, array $filterSettings, $constraintDates = true)
|
||||
{
|
||||
if ($constraintDates) {
|
||||
$this->addDateFilterConditions($query, $filterSettings);
|
||||
@@ -281,41 +241,21 @@ abstract class AbstractRepository extends \TYPO3\CMS\Extbase\Persistence\Reposit
|
||||
$this->addRoomTypesFilterConditions($query, $filterSettings);
|
||||
$this->addTransportationFilterConditions($query, $filterSettings);
|
||||
|
||||
// Default fulltext search in case autocompletion has not been used
|
||||
// if ((int) $filterSettings->getDestinationUid() === 0 && !empty($filterSettings->getDestinationName())) {
|
||||
// $query
|
||||
// ->andWhere($query->expr()->orX(
|
||||
// $query->expr()->like('LOWER(date.country_name) COLLATE utf8_bin',':destination'),
|
||||
// $query->expr()->like('LOWER(date.country_keywords) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.region_name) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.region_keywords) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.city_name) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.city_keywords) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.hotel_name) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.hotel_keywords) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.product_name) COLLATE utf8_bin', ':destination'),
|
||||
// $query->expr()->like('LOWER(date.product_keywords) COLLATE utf8_bin', ':destination')
|
||||
// ))
|
||||
// ->setParameter('destination', '%' . mb_strtolower($filterSettings->getDestinationName()) . '%')
|
||||
// ;
|
||||
// }
|
||||
|
||||
// Product
|
||||
if ((int) $filterSettings->getProductUid() > 0) {
|
||||
if (!empty($filterSettings['productUid'])) {
|
||||
$query
|
||||
->andWhere('date.product = :productUid')
|
||||
->setParameter('productUid', $filterSettings->getProductUid())
|
||||
->setParameter('productUid', (int)$filterSettings['productUid'])
|
||||
;
|
||||
}
|
||||
|
||||
// Hotel
|
||||
if ((int) $filterSettings->getHotelUid() > 0) {
|
||||
if (!empty($filterSettings['hotelUid'])) {
|
||||
$query
|
||||
->andWhere('date.hotel = :hotel')
|
||||
->setParameter(':hotel', $filterSettings->getHotelUid())
|
||||
->setParameter(':hotel', (int)$filterSettings['hotelUid'])
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Domain\Repository;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Model\Product;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
|
||||
@@ -73,7 +72,7 @@ class DateRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
public function getFilterOptions(
|
||||
FilterSettings $filterSettings,
|
||||
array $filterSettings,
|
||||
array $excludedConceptUids = [],
|
||||
array $excludedRegionUids = [],
|
||||
array $forcedConceptUids = []
|
||||
@@ -131,11 +130,11 @@ class DateRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function searchHotels($search, FilterSettings $filterSettings)
|
||||
public function searchHotels($search, array $filterSettings)
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -166,12 +165,12 @@ class DateRepository extends AbstractRepository
|
||||
|
||||
/**
|
||||
* @param string $search
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param array $excludedConceptUids
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function searchProducts($search, FilterSettings $filterSettings, $excludedConceptUids = [])
|
||||
public function searchProducts($search, array $filterSettings, $excludedConceptUids = [])
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -215,12 +214,12 @@ class DateRepository extends AbstractRepository
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $search
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param array $excludedRegionUids
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function searchDestinations($type, $search, FilterSettings $filterSettings, $excludedRegionUids = [])
|
||||
public function searchDestinations($type, $search, array $filterSettings, $excludedRegionUids = [])
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace EP\EpProducts\Domain\Repository;
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\Date;
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use EP\EpProducts\Domain\Model\Hotel;
|
||||
use EP\EpProducts\Domain\Model\Product;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
@@ -78,12 +77,12 @@ class ProductRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param array $excludedConceptUids
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function getSearchResult(FilterSettings $filterSettings, $excludedConceptUids = [])
|
||||
public function getSearchResult(array $filterSettings, $excludedConceptUids = [])
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -131,13 +130,13 @@ class ProductRepository extends AbstractRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param array $excludedConceptUids
|
||||
* @param int $limit
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function getTeasers(FilterSettings $filterSettings, $excludedConceptUids = [], $limit = 0)
|
||||
public function getTeasers(array $filterSettings, $excludedConceptUids = [], $limit = 0)
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -173,12 +172,12 @@ class ProductRepository extends AbstractRepository
|
||||
$query->setMaxResults($limit);
|
||||
}
|
||||
|
||||
if (!$filterSettings->getProductUid()) {
|
||||
if (empty($filterSettings['productUid'])) {
|
||||
$query->andWhere('product_searchable = 1');
|
||||
} else {
|
||||
$query
|
||||
->andWhere('product = :productUid')
|
||||
->setParameter('productUid', $filterSettings->getProductUid())
|
||||
->setParameter('productUid', (int)$filterSettings['productUid'])
|
||||
;
|
||||
}
|
||||
|
||||
@@ -195,18 +194,18 @@ class ProductRepository extends AbstractRepository
|
||||
;
|
||||
}
|
||||
|
||||
return $query->execute()->fetchAllAssociative(\PDO::FETCH_ASSOC);
|
||||
return $query->execute()->fetchAllAssociative();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Product $product
|
||||
* @param Hotel $hotel
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param Date $date
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function getPricetable(Product $product, Hotel $hotel, FilterSettings $filterSettings, Date $date = null)
|
||||
public function getPricetable(Product $product, Hotel $hotel, array $filterSettings, Date $date = null)
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
@@ -248,11 +247,11 @@ class ProductRepository extends AbstractRepository
|
||||
/**
|
||||
* @param Product $product
|
||||
* @param Hotel $hotel
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @return array
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function getAvailableDates(Product $product, Hotel $hotel, FilterSettings $filterSettings)
|
||||
public function getAvailableDates(Product $product, Hotel $hotel, array $filterSettings)
|
||||
{
|
||||
$qb = $this->getDbConnection()->createQueryBuilder();
|
||||
|
||||
|
||||
@@ -61,22 +61,23 @@ class FilterService implements SingletonInterface
|
||||
bool $respectBookableDaterange = true,
|
||||
string $dateFromYmd = null,
|
||||
string $dateToYmd = null
|
||||
): FilterSettings {
|
||||
): array {
|
||||
$filterSettings = [];
|
||||
|
||||
[ $dateFrom, $dateTo ] = $this->constrainDateRange($this->dateRepository->getDateRange());
|
||||
|
||||
if (null !== $dateFromYmd) {
|
||||
$dateFrom = new \DateTime($dateFromYmd);
|
||||
$filterSettings['dateFrom'] = $dateFromYmd;
|
||||
} elseif (!$respectBookableDaterange) {
|
||||
$dateFrom = new \DateTime('now');
|
||||
$dateFrom = (new \DateTime('now'))->format('Y-m-d');
|
||||
}
|
||||
|
||||
if (null !== $dateToYmd) {
|
||||
$dateTo = new \DateTime($dateToYmd);
|
||||
$filterSettings['dateTo'] = $dateToYmd;
|
||||
}
|
||||
|
||||
$filterSettings = new FilterSettings($dateFrom, $dateTo);
|
||||
if ($forcedConceptUids) {
|
||||
$filterSettings->setConceptUids($forcedConceptUids);
|
||||
$filterSettings['conceptUids'] = $forcedConceptUids;
|
||||
}
|
||||
|
||||
return $filterSettings;
|
||||
@@ -84,7 +85,7 @@ class FilterService implements SingletonInterface
|
||||
|
||||
public function getFilterOptions
|
||||
(
|
||||
FilterSettings $filterSettings,
|
||||
array $filterSettings,
|
||||
array $excludedConceptUids = [],
|
||||
array $excludedRegionUids = [],
|
||||
array $forcedConceptUids = []
|
||||
@@ -250,36 +251,36 @@ class FilterService implements SingletonInterface
|
||||
return [$dateFrom, $dateTo];
|
||||
}
|
||||
|
||||
public function getFiltersettingsFromSettings(array $settings): FilterSettings
|
||||
public function getFiltersettingsFromSettings(array $settings): array
|
||||
{
|
||||
$filterSettings = new FilterSettings();
|
||||
$filterSettings = [];
|
||||
if (isset($settings['regionUid']) && (int) $settings['regionUid'] > 0) {
|
||||
$filterSettings->setRegionUids([$settings['regionUid']]);
|
||||
$filterSettings['regionUids'] = [ $settings['regionUid'] ];
|
||||
} elseif (isset($settings['countryUid']) && (int) $settings['countryUid'] > 0) {
|
||||
$filterSettings->setCountryUids([$settings['countryUid']]);
|
||||
$filterSettings['countryUids'] = [ $settings['countryUid'] ];
|
||||
} elseif (isset($settings['hotelUid']) && (int) $settings['hotelUid'] > 0) {
|
||||
$filterSettings->setHotelUid($settings['hotelUid']);
|
||||
$filterSettings['hotelUid'] = $settings['hotelUid'];
|
||||
}
|
||||
$conceptUids = GeneralUtility::trimExplode(',', $settings['conceptUid'], true);
|
||||
if ($conceptUids) {
|
||||
$filterSettings->setConceptUids($conceptUids);
|
||||
$filterSettings['conceptUids'] = $conceptUids;
|
||||
}
|
||||
if (isset($settings['dateFrom']) && (int) $settings['dateFrom'] > 0) {
|
||||
$dateFrom = new \DateTime();
|
||||
$dateFrom->setTimestamp($settings['dateFrom']);
|
||||
$filterSettings->setDateFrom($dateFrom);
|
||||
$filterSettings['dateFrom'] = $dateFrom->format('Y-m-d');
|
||||
}
|
||||
if (isset($settings['dateTo']) && (int) $settings['dateTo'] > 0) {
|
||||
$dateTo = new \DateTime();
|
||||
$dateTo->setTimestamp($settings['dateTo']);
|
||||
$filterSettings->setDateTo($dateTo);
|
||||
$filterSettings['dateTo'] = $dateTo->format('Y-m-d');
|
||||
}
|
||||
if (isset($settings['busAvailable']) && (bool) $settings['busAvailable'] === true) {
|
||||
$filterSettings->setBus(true);
|
||||
$filterSettings['bus'] = true;
|
||||
}
|
||||
if (array_key_exists('hotelTypes', $settings)) {
|
||||
$hotelTypes = GeneralUtility::trimExplode(',', $settings['hotelTypes'], true);
|
||||
$filterSettings->setHotelTypes($hotelTypes);
|
||||
$filterSettings['hotelTypes'] = $hotelTypes;
|
||||
}
|
||||
|
||||
return $filterSettings;
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace EP\EpProducts\Service;
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
use EP\EpProducts\Domain\Model\FilterSettings;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
class FilterSettingsEncoder
|
||||
@@ -55,18 +54,20 @@ class FilterSettingsEncoder
|
||||
];
|
||||
}
|
||||
|
||||
public static function encode(FilterSettings $filterSettings): string
|
||||
public static function encode(array $filterSettings): string
|
||||
{
|
||||
$encoded = [];
|
||||
foreach (static::getMapping() as $property)
|
||||
{
|
||||
foreach (static::getMapping() as $property) {
|
||||
[$name, $type] = $property;
|
||||
$method = 'get' . ucfirst($name);
|
||||
$value = $filterSettings->{$method}();
|
||||
if (!isset($filterSettings[$name])) {
|
||||
$encoded[] = '';
|
||||
continue;
|
||||
}
|
||||
$value = $filterSettings[$name];
|
||||
if ($type === 'array') {
|
||||
$encoded[] = implode(';', $value);
|
||||
} elseif ($type === 'date') {
|
||||
$encoded[] = ($value !== null) ? $value->format('Y-m-d') : '';
|
||||
$encoded[] = $value ?? '';
|
||||
} else {
|
||||
$encoded[] = preg_replace('/[\r\n]/', '', $value);
|
||||
}
|
||||
|
||||
@@ -95,13 +95,13 @@ class ProductDataService implements SingletonInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FilterSettings $filterSettings
|
||||
* @param array $filterSettings
|
||||
* @param array $excludedConceptUids
|
||||
* @param int $limit
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTeasergroup(FilterSettings $filterSettings, $excludedConceptUids = [], $limit = 0)
|
||||
public function getTeasergroup(array $filterSettings, $excludedConceptUids = [], $limit = 0)
|
||||
{
|
||||
$teaserData = $this->productRepository->getTeasers($filterSettings, $excludedConceptUids);
|
||||
return $this->preprocessTeasergroup($teaserData, $limit);
|
||||
|
||||
@@ -66,7 +66,7 @@ class SearchResultService implements SingletonInterface
|
||||
$this->flagIconService = $flagIconService;
|
||||
}
|
||||
|
||||
public function getResult(FilterSettings $filterSettings, array $excludedConceptUids = []): SearchResult
|
||||
public function getResult(array $filterSettings, array $excludedConceptUids = []): SearchResult
|
||||
{
|
||||
$searchResultData = $this->productRepository->getSearchResult($filterSettings, $excludedConceptUids);
|
||||
|
||||
|
||||
@@ -48,16 +48,10 @@ class SearchResultUrlService implements SingletonInterface
|
||||
|
||||
/**
|
||||
* @param SearchResult $searchResult
|
||||
* @param array $options
|
||||
* @param int $defaultDetailPageUid
|
||||
*/
|
||||
public function process(SearchResult $searchResult, array $options): void
|
||||
public function process(SearchResult $searchResult, $defaultDetailPageUid): void
|
||||
{
|
||||
$optionsResolver = new OptionsResolver();
|
||||
$optionsResolver->setRequired([ 'defaultDetailPageUid' ]);
|
||||
$resolvedOptions = $optionsResolver->resolve($options);
|
||||
|
||||
$defaultDetailPageUid = $resolvedOptions['defaultDetailPageUid'];
|
||||
|
||||
$data = $searchResult->getData();
|
||||
|
||||
foreach ($data as $conceptUid => $conceptData) {
|
||||
|
||||
@@ -32,19 +32,21 @@ class DateUtility
|
||||
|
||||
public static function formatDateRange
|
||||
(
|
||||
\DateTime $dateFrom = null,
|
||||
\DateTime $dateTo = null,
|
||||
?string $dateFromString = '',
|
||||
?string $dateToString = '',
|
||||
bool $includeLabel = true,
|
||||
string $format = 'd.m.Y'
|
||||
): string {
|
||||
if ($dateFrom === null && $dateTo === null) {
|
||||
if (empty($dateFromString) && empty($dateToString)) {
|
||||
return '';
|
||||
}
|
||||
$dateFrom = new \DateTime($dateFromString);
|
||||
$dateTo = new \DateTime($dateToString);
|
||||
$label = $includeLabel ? 'im Zeitraum ' : '';
|
||||
if ($dateFrom !== null && $dateTo === null) {
|
||||
if ($dateFromString !== '' && $dateToString === '') {
|
||||
return $label . 'ab ' . $dateFrom->format($format);
|
||||
}
|
||||
if ($dateFrom === null && $dateTo !== null) {
|
||||
if ($dateFromString === '' && $dateToString !== '') {
|
||||
return $label . '-' . $dateTo->format($format);
|
||||
}
|
||||
return $label . $dateFrom->format($format) . '-' . $dateTo->format($format);
|
||||
|
||||
@@ -40,8 +40,8 @@ class DateRangeViewHelper extends AbstractViewHelper
|
||||
{
|
||||
parent::initializeArguments();
|
||||
|
||||
$this->registerArgument('dateFrom', 'mixed', 'Date range start');
|
||||
$this->registerArgument('dateTo', 'mixed', 'Date range end');
|
||||
$this->registerArgument('dateFrom', 'string', 'Date range start');
|
||||
$this->registerArgument('dateTo', 'string', 'Date range end');
|
||||
$this->registerArgument('includeLabel', 'Bool', 'Whether to include a label', false, true);
|
||||
$this->registerArgument('format', 'String', 'The date format', false, 'd.m.Y');
|
||||
}
|
||||
@@ -56,27 +56,12 @@ class DateRangeViewHelper extends AbstractViewHelper
|
||||
array $arguments,
|
||||
\Closure $renderChildrenClosure,
|
||||
RenderingContextInterface $renderingContext
|
||||
)
|
||||
{
|
||||
$dateFrom = static::ensureDateObject($arguments['dateFrom']);
|
||||
$dateTo = static::ensureDateObject($arguments['dateTo']);
|
||||
|
||||
) {
|
||||
return DateUtility::formatDateRange(
|
||||
$dateFrom,
|
||||
$dateTo,
|
||||
$arguments['dateFrom'],
|
||||
$arguments['dateTo'],
|
||||
$arguments['includeLabel'],
|
||||
$arguments['format']
|
||||
);
|
||||
}
|
||||
|
||||
private static function ensureDateObject($argument): \DateTime
|
||||
{
|
||||
if ($argument instanceof \DateTime) {
|
||||
$dateTime = $argument;
|
||||
} else {
|
||||
$dateTime = new \DateTime($argument);
|
||||
}
|
||||
|
||||
return $dateTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,12 @@ import Alpine from 'alpinejs'
|
||||
import parseAppConfig from './components/appconfig'
|
||||
import initSliders from './components/sliders'
|
||||
import appendPaCode from './components/pacode'
|
||||
import scrollTop from './components/scoll-top'
|
||||
import scrollTo from './components/scroll-to'
|
||||
import sticky from './components/sticky'
|
||||
import watchlistToggle from './components/watchlist-toggle'
|
||||
import watchlistButton from './components/watchlist-button'
|
||||
import watchlist from './components/watchlist'
|
||||
import collapse from './components/collapse'
|
||||
import fbpixel from './components/fbpixel'
|
||||
import ajaxContent from './components/ajax-content'
|
||||
import faq from './components/faq'
|
||||
import osmMap from './components/osm-map'
|
||||
import productDetail from './components/product-detail'
|
||||
import daytripDateSelect from './components/daytrip-date-select'
|
||||
import searchBar from './components/search-bar'
|
||||
import search from './components/search'
|
||||
import calendar from './components/calendar'
|
||||
import contactForm from './components/contact-form'
|
||||
|
||||
@@ -54,21 +45,12 @@ Alpine.store('show', {
|
||||
Alpine.store('watchlist', storedWatchlist)
|
||||
Alpine.store('filterSettings', defaultFilterSettings)
|
||||
Alpine.store('filterOptions', defaultFilterOptions)
|
||||
Alpine.data('scrollTop', scrollTop)
|
||||
Alpine.data('scrollTo', scrollTo)
|
||||
Alpine.data('sticky', sticky)
|
||||
Alpine.data('watchlistToggle', watchlistToggle)
|
||||
Alpine.data('watchlistButton', watchlistButton)
|
||||
Alpine.data('watchlist', watchlist)
|
||||
Alpine.data('collapse', collapse)
|
||||
Alpine.data('fbpixel', fbpixel)
|
||||
Alpine.data('ajaxContent', ajaxContent)
|
||||
Alpine.data('faq', faq)
|
||||
Alpine.data('osmMap', osmMap)
|
||||
Alpine.data('productDetail', productDetail)
|
||||
Alpine.data('daytripDateSelect', daytripDateSelect)
|
||||
Alpine.data('searchBar', searchBar)
|
||||
Alpine.data('search', search)
|
||||
Alpine.data('calendar', calendar)
|
||||
Alpine.data('contactForm', contactForm)
|
||||
Alpine.start()
|
||||
@@ -77,3 +59,10 @@ Glightbox({
|
||||
selector: 'data-glightbox',
|
||||
slideEffect: 'fade',
|
||||
})
|
||||
|
||||
import { Application } from 'stimulus'
|
||||
import { definitionsFromContext } from 'stimulus/webpack-helpers'
|
||||
|
||||
const application = Application.start()
|
||||
const context = require.context('./controllers', true, /\.js$/)
|
||||
application.load(definitionsFromContext(context))
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import axios from 'axios'
|
||||
import { debounce } from './throttle-debounce'
|
||||
|
||||
export default uri => ({
|
||||
uri,
|
||||
loaded: false,
|
||||
loading: false,
|
||||
init() {
|
||||
window.addEventListener('scroll', debounce(() => {
|
||||
if (true === this.loaded) {
|
||||
return
|
||||
}
|
||||
this.load()
|
||||
}, 250))
|
||||
},
|
||||
load() {
|
||||
if (false === this.inViewport()) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
axios({
|
||||
url: this.uri,
|
||||
responseType: 'text'
|
||||
}).then((response) => {
|
||||
this.loaded = true
|
||||
this.$refs.container.innerHTML = response.data
|
||||
this.$refs.trigger.remove()
|
||||
}).catch(() => {
|
||||
this.loaded = false
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
inViewport () {
|
||||
const rect = this.$refs.trigger.getBoundingClientRect()
|
||||
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { debounce } from './throttle-debounce'
|
||||
|
||||
export default (isCollapsed = true, until = null, from = null) => ({
|
||||
collapsed: isCollapsed,
|
||||
collapsedUntil: until,
|
||||
collapsedFrom: from,
|
||||
init() {
|
||||
this.checkBreakpoints()
|
||||
window.addEventListener('resize', debounce(() => {
|
||||
this.checkBreakpoints()
|
||||
}, 250))
|
||||
},
|
||||
checkBreakpoints() {
|
||||
if (null !== this.collapsedUntil) {
|
||||
this.collapsed = window.innerWidth < parseInt(this.collapsedUntil)
|
||||
}
|
||||
if (null !== this.collapsedFrom) {
|
||||
this.collapsed = window.innerWidth > parseInt(this.collapsedFrom)
|
||||
}
|
||||
},
|
||||
trigger: {
|
||||
['@click.prevent']() {
|
||||
this.collapsed = !this.collapsed
|
||||
},
|
||||
},
|
||||
container: {
|
||||
['x-show']() {
|
||||
return this.collapsed === false
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { debounce } from './throttle-debounce'
|
||||
|
||||
export default () => ({
|
||||
query: '',
|
||||
init() {
|
||||
this.entries = this.$el.querySelectorAll('[x-data^="collapse"]')
|
||||
this.$watch('query', debounce(value => {
|
||||
if (value.length < 3) {
|
||||
this.entries.forEach(entry => {
|
||||
entry.classList.remove('hidden')
|
||||
})
|
||||
return
|
||||
}
|
||||
const pattern = new RegExp(value, 'i')
|
||||
this.entries.forEach(entry => {
|
||||
const question = entry.childNodes[0].textContent
|
||||
const answer = entry.childNodes[1].textContent
|
||||
if (!question.match(pattern) && !answer.match(pattern)) {
|
||||
entry.classList.add('hidden')
|
||||
}
|
||||
})
|
||||
}, 200))
|
||||
},
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
export default props => ({
|
||||
init() {
|
||||
fbq('track', 'ViewContent', props)
|
||||
}
|
||||
})
|
||||
@@ -1,52 +0,0 @@
|
||||
import L from 'leaflet';
|
||||
|
||||
export default () => ({
|
||||
init() {
|
||||
this.lat = this.$el.dataset.lat
|
||||
this.lng = this.$el.dataset.lng
|
||||
this.zoom = this.$el.dataset.zoom || 12
|
||||
this.externalLink = this.$el.dataset.link
|
||||
this.markers = this.$el.dataset.markers ? JSON.parse(this.$el.dataset.markers) : []
|
||||
this.map = L.map(this.$el, {
|
||||
scrollWheelZoom: false
|
||||
});
|
||||
this.map.setZoom(this.zoom);
|
||||
if (this.lat && this.lng) {
|
||||
const position = L.latLng(this.lat, this.lng);
|
||||
const marker = L.marker(position);
|
||||
if (this.externalLink) {
|
||||
marker.on('click', () => {
|
||||
window.open(this.externalLink, '_blank')
|
||||
});
|
||||
} else {
|
||||
marker.on('click', () => {
|
||||
window.open('https://www.google.com/maps/search/?api=1&zoom=15&query=' + this.lat + ',' + this.lng, '_blank')
|
||||
});
|
||||
}
|
||||
marker.addTo(this.map);
|
||||
this.map.setView(position);
|
||||
} else if (this.markers.length) {
|
||||
const bounds = L.latLngBounds();
|
||||
for (let item of this.markers) {
|
||||
const position = L.latLng(item.lat, item.lng);
|
||||
const marker = L.marker(position);
|
||||
if (item.externalLink) {
|
||||
marker.on('click', () => {
|
||||
window.open(item.externalLink, '_blank');
|
||||
});
|
||||
} else {
|
||||
const popupContent = '<div><strong>' + item.name + '</strong><br>' + item.teaser +
|
||||
'<br><a href="' + item.uri + '">zu den Details</a></div>';
|
||||
marker.bindPopup(popupContent);
|
||||
}
|
||||
marker.addTo(this.map);
|
||||
bounds.extend(position);
|
||||
}
|
||||
this.map.fitBounds(bounds);
|
||||
}
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(this.map);
|
||||
}
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
import { debounce } from './throttle-debounce'
|
||||
|
||||
export default () => ({
|
||||
show: false,
|
||||
trigger: {
|
||||
['@click.prevent']() {
|
||||
window.scroll({top: 0, left: 0, behavior: 'smooth'})
|
||||
},
|
||||
['x-show']() {
|
||||
return this.show
|
||||
}
|
||||
},
|
||||
init() {
|
||||
window.addEventListener('scroll', debounce(() => {
|
||||
this.show = window.scrollY > window.innerHeight / 3
|
||||
}, 250))
|
||||
},
|
||||
})
|
||||
@@ -1,9 +0,0 @@
|
||||
export default targetId => ({
|
||||
targetId,
|
||||
init() {
|
||||
this.$el.addEventListener('click', e => {
|
||||
e.preventDefault()
|
||||
document.getElementById(this.targetId).scrollIntoView({behavior: 'smooth'})
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,11 +0,0 @@
|
||||
import { debounce } from './throttle-debounce'
|
||||
|
||||
export default offset => ({
|
||||
offset,
|
||||
sticky: false,
|
||||
init() {
|
||||
window.addEventListener('scroll', debounce(() => {
|
||||
this.sticky = window.scrollY > this.offset
|
||||
}, 250))
|
||||
}
|
||||
})
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDebounce } from 'stimulus-use'
|
||||
import { useFetch} from '../mixins/use_fetch'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static debounces = [ 'load' ]
|
||||
|
||||
static values = {
|
||||
uri: String,
|
||||
loading: Boolean,
|
||||
loaded: Boolean,
|
||||
}
|
||||
|
||||
static targets = [ 'container', 'trigger' ]
|
||||
|
||||
initialize() {
|
||||
useDebounce(this)
|
||||
useFetch(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
window.addEventListener('scroll', () => {
|
||||
this.load()
|
||||
})
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
window.removeEventListener('scroll', () => {
|
||||
this.load()
|
||||
})
|
||||
}
|
||||
|
||||
load() {
|
||||
if (true === this.loadedValue) {
|
||||
return
|
||||
}
|
||||
if (false === this.inViewport()) {
|
||||
return
|
||||
}
|
||||
this.loadHtml()
|
||||
}
|
||||
|
||||
loadHtml() {
|
||||
this.loadingValue = true
|
||||
fetch(this.uriValue)
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseHTML)
|
||||
.then(html => {
|
||||
this.loadedValue = true
|
||||
this.containerTarget.innerHTML = html
|
||||
this.triggerTarget.remove()
|
||||
})
|
||||
.catch(() => {
|
||||
this.loadedValue = false
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadingValue = false
|
||||
})
|
||||
}
|
||||
|
||||
inViewport() {
|
||||
const rect = this.triggerTarget.getBoundingClientRect()
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useWindowResize, useDebounce } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static debounces = [ 'checkBreakpoints' ]
|
||||
|
||||
static values = {
|
||||
collapsed: Boolean,
|
||||
from: Number,
|
||||
until: Number,
|
||||
}
|
||||
|
||||
static targets = [ 'container', 'icon' ]
|
||||
|
||||
initialize() {
|
||||
useWindowResize(this)
|
||||
useDebounce(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.toggleByWindowWidth(window.innerWidth)
|
||||
}
|
||||
|
||||
windowResize({ width, height, event }) {
|
||||
this.toggleByWindowWidth(width)
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.collapsedValue = !this.collapsedValue
|
||||
}
|
||||
|
||||
toggleByWindowWidth(width) {
|
||||
if (0 !== this.untilValue) {
|
||||
this.collapsedValue = width < this.untilValue
|
||||
}
|
||||
if (0 !== this.fromValue) {
|
||||
this.collapsedValue = width > this.fromValue
|
||||
}
|
||||
}
|
||||
|
||||
collapsedValueChanged() {
|
||||
this.containerTarget.classList.toggle('hidden', this.collapsedValue === true)
|
||||
if (this.hasIconTarget) {
|
||||
this.iconTarget.classList.toggle('rotate-180', this.collapsedValue === false)
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDispatch } from 'stimulus-use'
|
||||
import flatpickr from 'flatpickr'
|
||||
import { German } from 'flatpickr/dist/l10n/de'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static targets = [ 'field' ]
|
||||
|
||||
static values = {
|
||||
minDate: String,
|
||||
dateFrom: String,
|
||||
dateTo: String,
|
||||
}
|
||||
|
||||
initialize() {
|
||||
useDispatch(this)
|
||||
|
||||
this.picker = flatpickr(this.fieldTarget, {
|
||||
mode: 'range',
|
||||
locale: German,
|
||||
inline: true,
|
||||
minDate: this.minDateValue,
|
||||
onChange: selectedDates => {
|
||||
if (selectedDates.length === 2) {
|
||||
this.dispatch('range-select', {
|
||||
from: flatpickr.formatDate(selectedDates[0], 'Y-m-d'),
|
||||
to: flatpickr.formatDate(selectedDates[1], 'Y-m-d'),
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connect() {
|
||||
if ('' !== this.dateFromValue && '' !== this.dateToValue) {
|
||||
let dateFrom = new Date(this.dateFromValue)
|
||||
let dateTo = new Date(this.dateToValue)
|
||||
this.picker.setDate([ dateFrom, dateTo ])
|
||||
}
|
||||
}
|
||||
|
||||
selectPreset(e) {
|
||||
e.preventDefault()
|
||||
const preset = JSON.parse(e.currentTarget.dataset.preset)
|
||||
this.picker.setDate([ new Date(preset.dateFrom), new Date(preset.dateTo) ])
|
||||
this.dispatch('range-select', { from: preset.dateFrom, to: preset.dateTo })
|
||||
}
|
||||
|
||||
reset(e) {
|
||||
e.preventDefault()
|
||||
this.picker.clear()
|
||||
this.picker.jumpToDate()
|
||||
this.dispatch('range-select', { from: null, to: null })
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDebounce } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static debounces = [ 'filter' ]
|
||||
|
||||
static targets = [ 'entry' ]
|
||||
|
||||
initialize() {
|
||||
useDebounce(this)
|
||||
}
|
||||
|
||||
filter(e) {
|
||||
let value = e.target.value
|
||||
if (value.length < 3) {
|
||||
this.entryTargets.forEach(entry => {
|
||||
entry.classList.remove('hidden')
|
||||
})
|
||||
return
|
||||
}
|
||||
const pattern = new RegExp(value, 'i')
|
||||
this.entryTargets.forEach(entry => {
|
||||
const question = entry.childNodes[0].textContent
|
||||
const answer = entry.childNodes[1].textContent
|
||||
if (!question.match(pattern) && !answer.match(pattern)) {
|
||||
entry.classList.add('hidden')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Controller } from 'stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = { props: Object }
|
||||
|
||||
connect() {
|
||||
fbq('track', 'ViewContent', this.propsValue)
|
||||
}
|
||||
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import L from 'leaflet'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = {
|
||||
lat: Number,
|
||||
lng: Number,
|
||||
zoom: Number,
|
||||
externalLink: String,
|
||||
markers: Array,
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.map = L.map(this.element, {
|
||||
scrollWheelZoom: false
|
||||
})
|
||||
const zoom = this.hasZoomValue ? this.zoomValue : 12
|
||||
this.map.setZoom(zoom)
|
||||
if (this.hasLatValue && this.hasLngValue) {
|
||||
const position = L.latLng(this.latValue, this.lngValue)
|
||||
const marker = L.marker(position)
|
||||
if (this.hasExternalLinkValue) {
|
||||
marker.on('click', () => {
|
||||
window.open(this.externalLinkValue, '_blank')
|
||||
})
|
||||
} else {
|
||||
marker.on('click', () => {
|
||||
window.open('https://www.google.com/maps/search/?api=1&zoom=15&query=' + this.latValue + ',' + this.lngValue, '_blank')
|
||||
})
|
||||
}
|
||||
marker.addTo(this.map)
|
||||
this.map.setView(position)
|
||||
} else if (this.hasMarkersValue && this.markersValue.length) {
|
||||
const bounds = L.latLngBounds()
|
||||
for (let item of this.markers) {
|
||||
const position = L.latLng(item.lat, item.lng)
|
||||
const marker = L.marker(position)
|
||||
if (item.externalLink) {
|
||||
marker.on('click', () => {
|
||||
window.open(item.externalLink, '_blank')
|
||||
})
|
||||
} else {
|
||||
const popupContent = '<div><strong>' + item.name + '</strong><br>' + item.teaser +
|
||||
'<br><a href="' + item.uri + '">zu den Details</a></div>'
|
||||
marker.bindPopup(popupContent)
|
||||
}
|
||||
marker.addTo(this.map)
|
||||
bounds.extend(position)
|
||||
}
|
||||
this.map.fitBounds(bounds)
|
||||
}
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(this.map)
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { Controller } from 'stimulus'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = { target: String }
|
||||
|
||||
scroll() {
|
||||
document.getElementById(this.targetValue).scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDebounce} from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static debounces = [ 'toggle' ]
|
||||
|
||||
initialize() {
|
||||
useDebounce(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
window.addEventListener('scroll', () => {
|
||||
this.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.element.classList.toggle('hidden', window.scrollY < window.innerHeight / 3)
|
||||
}
|
||||
|
||||
scroll() {
|
||||
window.scroll({ top: 0, left: 0, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDispatch } from 'stimulus-use'
|
||||
import { useFetch } from '../mixins/use_fetch'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static values = {
|
||||
loading: Boolean,
|
||||
uri: String,
|
||||
}
|
||||
|
||||
static targets = [
|
||||
'form',
|
||||
'resultsContainer',
|
||||
'loadingIndicator',
|
||||
'dateFrom',
|
||||
'dateTo',
|
||||
]
|
||||
|
||||
initialize() {
|
||||
useFetch(this)
|
||||
useDispatch(this)
|
||||
}
|
||||
|
||||
filter() {
|
||||
this.loadingValue = true
|
||||
let data = new FormData(this.formTarget)
|
||||
fetch(this.uriValue, { method: 'POST', body: data })
|
||||
.then(this.checkStatus)
|
||||
.then(this.parseHTML)
|
||||
.then(html => {
|
||||
this.resultsContainerTarget.innerHTML = html
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadingValue = false
|
||||
this.element.scrollIntoView({ behavior: 'smooth' })
|
||||
})
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.filter()
|
||||
}
|
||||
|
||||
selectDateRange(e) {
|
||||
this.dateFromTarget.value = e.detail.from
|
||||
this.dateToTarget.value = e.detail.to
|
||||
this.filter()
|
||||
}
|
||||
|
||||
loadingValueChanged() {
|
||||
this.loadingIndicatorTarget.classList.toggle('hidden', this.loadingValue === false)
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { Controller } from 'stimulus'
|
||||
import { useDebounce } from 'stimulus-use'
|
||||
|
||||
export default class extends Controller {
|
||||
|
||||
static debounces = [ 'toggle' ]
|
||||
|
||||
static classes = [ 'sticky' ]
|
||||
|
||||
static values = {
|
||||
sticky: Boolean,
|
||||
offset: Number,
|
||||
}
|
||||
|
||||
initialize() {
|
||||
useDebounce(this)
|
||||
}
|
||||
|
||||
connect() {
|
||||
window.addEventListener('scroll', () => {
|
||||
this.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
window.removeEventListener('scroll', () => {
|
||||
this.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.stickyValue = window.scrollY > this.offsetValue
|
||||
}
|
||||
|
||||
stickyValueChanged() {
|
||||
this.element.classList.toggle(this.stickyClass, this.stickyValue)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export const useConfig = controller => {
|
||||
Object.assign(controller, {
|
||||
getConfig() {
|
||||
const configElement = document.getElementById('appconfig')
|
||||
if (null === configElement) {
|
||||
return {}
|
||||
}
|
||||
return JSON.parse(configElement.innerHTML)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export const useDebounce = controller => {
|
||||
Object.assign(controller, {
|
||||
debounce(callback, interval) {
|
||||
let debounceId
|
||||
return (...args) => {
|
||||
clearTimeout(debounceId)
|
||||
debounceId = setTimeout(() => callback.apply(this, args), interval)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'whatwg-fetch'
|
||||
|
||||
export const useFetch = controller => {
|
||||
Object.assign(controller, {
|
||||
checkStatus(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response
|
||||
} else {
|
||||
const error = new Error(response.statusText)
|
||||
error.response = response
|
||||
throw error
|
||||
}
|
||||
},
|
||||
parseJSON(response) {
|
||||
return response.json()
|
||||
},
|
||||
parseHTML(response) {
|
||||
return response.text()
|
||||
}
|
||||
})
|
||||
}
|
||||
+2
-18
@@ -1,27 +1,11 @@
|
||||
.bg-winter {
|
||||
@apply relative bg-cover bg-no-repeat bg-center;
|
||||
@apply bg-cover bg-no-repeat bg-center;
|
||||
background-image: url(../../images/background/winter.jpg);
|
||||
z-index: -5;
|
||||
|
||||
&:before {
|
||||
@apply block absolute top-0 left-0 inset-0 opacity-80;
|
||||
@apply bg-ep-primary sbw:bg-sbw-primary uch:bg-uch-primary;
|
||||
z-index: -10;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.bg-pattern {
|
||||
@apply relative bg-cover bg-no-repeat bg-center;
|
||||
@apply bg-cover bg-no-repeat bg-center;
|
||||
background-image: url(../../images/background/pattern.jpg);
|
||||
z-index: -5;
|
||||
|
||||
&:before {
|
||||
@apply block absolute top-0 left-0 inset-0 opacity-80;
|
||||
@apply bg-ep-primary sbw:bg-sbw-primary uch:bg-uch-primary;
|
||||
z-index: -10;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.bg-header-season-w {
|
||||
|
||||
@@ -4,64 +4,66 @@
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="bg-winter my-2">
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="relative container grid grid-cols-2 gap-8" data-rte-content>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="relative max-w-md mx-auto mb-8">
|
||||
<f:if condition="{image}">
|
||||
<f:then>
|
||||
<img class="block rounded-full w-full h-auto lazyload"
|
||||
data-src="{f:uri.image(image: image, width: '400', height: '400c')}"
|
||||
alt="{image.alternative}" title="{image.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
<a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}"
|
||||
rel="nofollow"
|
||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 -mb-8 rounded-full py-4 px-6 bg-ep-secondary sbw:bg-sbw-secondary uch:bg-uch-secondary flex items-center space-x-2">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-center text-white">
|
||||
<div class="bg-ep-primary-80">
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="relative container grid grid-cols-2 gap-8" data-rte-content>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="relative max-w-md mx-auto mb-8">
|
||||
<f:if condition="{image}">
|
||||
<f:then>
|
||||
<img class="block rounded-full w-full h-auto lazyload"
|
||||
data-src="{f:uri.image(image: image, width: '400', height: '400c')}"
|
||||
alt="{image.alternative}" title="{image.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
<a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}"
|
||||
rel="nofollow"
|
||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 -mb-8 rounded-full py-4 px-6 bg-ep-secondary sbw:bg-sbw-secondary uch:bg-uch-secondary flex items-center space-x-2">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-center text-white">
|
||||
<span class="block whitespace-nowrap">
|
||||
Ihr habt Fragen?
|
||||
</span>
|
||||
<span class="block whitespace-nowrap">
|
||||
<span class="block whitespace-nowrap">
|
||||
Wir helfen euch gerne!
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<span class="headline--secondary font-light text-white">
|
||||
{data.subheadline}
|
||||
</span>
|
||||
<span class="headline--secondary uppercase font-light text-white mb-4">
|
||||
<span class="headline--secondary uppercase font-light text-white mb-4">
|
||||
{data.headline}
|
||||
</span>
|
||||
<div class="text-white" data-rte-content>
|
||||
{data.bodytext -> f:format.html()}
|
||||
</div>
|
||||
<f:if condition="{data.flexForm.buttons}">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 lg:gap-8">
|
||||
<f:for each="{data.flexForm.buttons}" as="button">
|
||||
<f:link.typolink parameter="{button.container.link}"
|
||||
class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:for>
|
||||
<div class="text-white" data-rte-content>
|
||||
{data.bodytext -> f:format.html()}
|
||||
</div>
|
||||
</f:if>
|
||||
<f:if condition="{data.flexForm.buttons}">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 lg:gap-8">
|
||||
<f:for each="{data.flexForm.buttons}" as="button">
|
||||
<f:link.typolink parameter="{button.container.link}"
|
||||
class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:for>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -3,39 +3,41 @@
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="bg-winter my-2">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
<div class="max-w-screen-lg mx-auto" data-rte-content>
|
||||
<div class="md:flex md:items-start py-4">
|
||||
<div class="px-16 pb-4 md:px-4 md:pb-0 md:w-1/6">
|
||||
<f:if condition="{image}">
|
||||
<f:then>
|
||||
<img class="block w-full h-auto rounded-full lazyload"
|
||||
data-src="{f:uri.image(image: image, width: '400', height: '400c')}"
|
||||
alt="{image.alternative}" title="{image.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img class="block w-full h-auto rounded-full" src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
<div class="px-16 md:px-4 md:w-5/6 md:pl-8 text-center text-white md:text-left" data-rte-content>
|
||||
<div class="bg-ep-primary-80 sbw:bg-sbw-primary-80 uch:bg-uch-primary-80">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
<div class="max-w-screen-lg mx-auto" data-rte-content>
|
||||
<div class="md:flex md:items-start py-4">
|
||||
<div class="px-16 pb-4 md:px-4 md:pb-0 md:w-1/6">
|
||||
<f:if condition="{image}">
|
||||
<f:then>
|
||||
<img class="block w-full h-auto rounded-full lazyload"
|
||||
data-src="{f:uri.image(image: image, width: '400', height: '400c')}"
|
||||
alt="{image.alternative}" title="{image.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img class="block w-full h-auto rounded-full" src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
<div class="px-16 md:px-4 md:w-5/6 md:pl-8 text-center text-white md:text-left" data-rte-content>
|
||||
<span class="headline--3 text-white uppercase">
|
||||
{data.headline}
|
||||
</span>
|
||||
{data.text -> f:format.html()}
|
||||
<div class="flex flex-col space-y-4 md:flex-row md:items-center md:space-y-0 md:space-x-4">
|
||||
<f:for each="{data.flexForm.buttons}" as="button" iteration="iteration">
|
||||
<f:if condition="{iteration.cycle} <= 3">
|
||||
<f:link.typolink parameter="{button.container.link}" class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:if>
|
||||
</f:for>
|
||||
{data.text -> f:format.html()}
|
||||
<div class="flex flex-col space-y-4 md:flex-row md:items-center md:space-y-0 md:space-x-4">
|
||||
<f:for each="{data.flexForm.buttons}" as="button" iteration="iteration">
|
||||
<f:if condition="{iteration.cycle} <= 3">
|
||||
<f:link.typolink parameter="{button.container.link}" class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -5,65 +5,66 @@
|
||||
|
||||
|
||||
<div class="bg-winter my-2">
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="relative container grid grid-cols-2 gap-8 py-8" data-rte-content>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="relative max-w-md mx-auto mb-8">
|
||||
<f:if condition="{teaserImage}">
|
||||
<f:then>
|
||||
<img class="block rounded-full w-full h-auto lazyload"
|
||||
data-src="{f:uri.image(image: teaserImage, width: '400', height: '400c')}"
|
||||
alt="{teaserImage.alternative}" title="{teaserImage.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
<a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}"
|
||||
rel="nofollow"
|
||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 -mb-8 rounded-full py-4 px-6 bg-ep-secondary sbw:bg-sbw-secondary uch:bg-uch-secondary flex items-center space-x-2">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-center text-white">
|
||||
<div class="bg-ep-primary-80">
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="relative container grid grid-cols-2 gap-8 py-8" data-rte-content>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="relative max-w-md mx-auto mb-8">
|
||||
<f:if condition="{teaserImage}">
|
||||
<f:then>
|
||||
<img class="block rounded-full w-full h-auto lazyload"
|
||||
data-src="{f:uri.image(image: teaserImage, width: '400', height: '400c')}"
|
||||
alt="{teaserImage.alternative}" title="{teaserImage.title}">
|
||||
</f:then>
|
||||
<f:else>
|
||||
<img src="https://placehold.it/800x600" alt="">
|
||||
</f:else>
|
||||
</f:if>
|
||||
<a href="tel:{settings.phoneNumber -> v:format.pregReplace(pattern: '/[\-\s]/', replacement: '')}"
|
||||
rel="nofollow"
|
||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 -mb-8 rounded-full py-4 px-6 bg-ep-secondary sbw:bg-sbw-secondary uch:bg-uch-secondary flex items-center space-x-2">
|
||||
<svg class="w-10 h-10 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-center text-white">
|
||||
<span class="block whitespace-nowrap">
|
||||
Ihr habt Fragen?
|
||||
</span>
|
||||
<span class="block whitespace-nowrap">
|
||||
<span class="block whitespace-nowrap">
|
||||
Wir helfen euch gerne!
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<span class="headline--secondary font-light text-white">
|
||||
{data.subheader}
|
||||
</span>
|
||||
<span class="headline--secondary uppercase font-light text-white mb-4">
|
||||
<span class="headline--secondary uppercase font-light text-white mb-4">
|
||||
{data.header}
|
||||
</span>
|
||||
<div class="text-white" data-rte-content>
|
||||
{data.bodytext -> f:format.html()}
|
||||
</div>
|
||||
<f:if condition="{data.flexForm.buttons}">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 lg:gap-8">
|
||||
<f:for each="{data.flexForm.buttons}" as="button">
|
||||
<f:link.typolink parameter="{button.container.link}"
|
||||
class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:for>
|
||||
<div class="text-white" data-rte-content>
|
||||
{data.bodytext -> f:format.html()}
|
||||
</div>
|
||||
</f:if>
|
||||
<f:if condition="{data.flexForm.buttons}">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 lg:gap-8">
|
||||
<f:for each="{data.flexForm.buttons}" as="button">
|
||||
<f:link.typolink parameter="{button.container.link}"
|
||||
class="button shadow-lg">
|
||||
{button.container.label}
|
||||
</f:link.typolink>
|
||||
</f:for>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
</div>
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block sbw:md:hidden transform translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom1" arguments="{classes: 'hidden md:block sbw:md:hidden transform translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
</div>
|
||||
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,58 +1,57 @@
|
||||
<f:if condition="{faqs}">
|
||||
<div class="bg-winter my-2">
|
||||
<f:render partial="Disrupter/Outlines" section="Top2" arguments="{classes: 'hidden md:block transform -translate-y-2 text-white'}"/>
|
||||
<div class="container" x-data="faq">
|
||||
<span class="headline--3 text-white uppercase mb-4">
|
||||
Häufig gestellte Fragen
|
||||
</span>
|
||||
<f:if condition="{filter}">
|
||||
<div class="mb-4 pr-2 w-80 border-b border-r border-white flex items-center justify-between">
|
||||
<input type="text"
|
||||
x-model="query"
|
||||
class="bg-transparent border-0 uppercase ring-0 focus:ring-0 placeholder-white text-white"
|
||||
placeholder="Suchbegriff">
|
||||
<svg class="w-6 h-6 text-white">
|
||||
<use href="#icon-search"></use>
|
||||
</svg>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:for each="{faqs}" as="faq" iteration="iteration">
|
||||
<div class="p-4 rounded mb-2"
|
||||
x-data="collapse(<f:format.raw>{f:if(condition: iteration.isFirst, then: 'false', else: 'true')}</f:format.raw>)"
|
||||
x-bind:class="collapsed || 'bg-ep-primary-80'">
|
||||
<button class="uppercase text-lg text-white flex items-center space-x-4" x-bind="trigger">
|
||||
<svg class="w-4 h-4 text-white transform transition duration-200 ease-in-out" x-bind:class="collapsed || 'rotate-180'">
|
||||
<use href="#icon-chevron-down"></use>
|
||||
<div class="bg-ep-primary-80 sbw:bg-sbw-primary-80 uch:bg-uch-primary-80">
|
||||
<f:render partial="Disrupter/Outlines" section="Top2" arguments="{classes: 'hidden md:block text-white'}"/>
|
||||
<div class="container"
|
||||
data-controller="faq">
|
||||
<span class="headline--3 text-white uppercase mb-4">
|
||||
Häufig gestellte Fragen
|
||||
</span>
|
||||
<f:if condition="{filter}">
|
||||
<div class="mb-4 pr-2 w-80 border-b border-r border-white flex items-center justify-between">
|
||||
<input type="text"
|
||||
data-action="faq#filter"
|
||||
class="bg-transparent border-0 uppercase ring-0 focus:ring-0 placeholder-white text-white"
|
||||
placeholder="Suchbegriff">
|
||||
<svg class="w-6 h-6 text-white">
|
||||
<use href="#icon-search"></use>
|
||||
</svg>
|
||||
<span>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:for each="{faqs}" as="faq" iteration="iteration">
|
||||
<div class="p-4 rounded mb-2"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: iteration.isFirst, then: 'false', else: 'true')}"
|
||||
data-faq-target="entry">
|
||||
<button class="uppercase text-lg text-white flex items-center space-x-4"
|
||||
data-action="collapse#toggle">
|
||||
<svg class="w-4 h-4 text-white transform transition duration-200 ease-in-out"
|
||||
data-collapse-target="icon">
|
||||
<use href="#icon-chevron-down"></use>
|
||||
</svg>
|
||||
<span>
|
||||
{faq.question}
|
||||
</span>
|
||||
</button>
|
||||
<div class="text-white pt-2"
|
||||
x-bind="container"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-300"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
x-cloak
|
||||
data-rte-content>
|
||||
{faq.answer -> f:format.html()}
|
||||
</button>
|
||||
<div class="text-white pt-2 transition-transform ease-out overflow-hidden origin-top transform hidden"
|
||||
data-collapse-target="container"
|
||||
data-rte-content>
|
||||
{faq.answer -> f:format.html()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</f:for>
|
||||
<f:if condition="{settings.infoPageUid}">
|
||||
<div class="flex items-center justify-end space-x-2 text-sm text-white">
|
||||
<svg class="w-4 h-4">
|
||||
<use href="#icon-info-circle"></use>
|
||||
</svg>
|
||||
<a href="{f:uri.typolink(parameter: settings.infoPageUid)}">
|
||||
Weitere Reiseinfos findet ihr <span class="underline">hier</span>
|
||||
</a>
|
||||
</div>
|
||||
</f:if>
|
||||
</f:for>
|
||||
<f:if condition="{settings.infoPageUid}">
|
||||
<div class="flex items-center justify-end space-x-2 text-sm text-white">
|
||||
<svg class="w-4 h-4">
|
||||
<use href="#icon-info-circle"></use>
|
||||
</svg>
|
||||
<a href="{f:uri.typolink(parameter: settings.infoPageUid)}">
|
||||
Weitere Reiseinfos findet ihr <span class="underline">hier</span>
|
||||
</a>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom2" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
<f:render partial="Disrupter/Outlines" section="Bottom2" arguments="{classes: 'hidden md:block transform translate-y-2 text-white'}"/>
|
||||
</div>
|
||||
</f:if>
|
||||
|
||||
@@ -4,93 +4,95 @@
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<div class="bg-winter mt-8">
|
||||
<f:if condition="{settings.themeKey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="py-4 relative">
|
||||
<div class="container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 pb-4">
|
||||
<v:menu pageUid="{settings.footerNavPid}" levels="2" expandAll="true">
|
||||
<f:for each="{menu}" as="menuheader" iteration="iteration">
|
||||
<div x-data="collapse(true, 991)">
|
||||
<span class="headline--4 text-white hidden md:block">
|
||||
{menuheader.title}
|
||||
</span>
|
||||
<button class="headline--4 text-white md:hidden"
|
||||
x-bind="trigger">
|
||||
{menuheader.title}
|
||||
</button>
|
||||
<v:menu pageUid="{menuheader.uid}" useShortcutUid="true" as="submenu">
|
||||
<ul class="list-disc text-white mb-0 pl-4"
|
||||
x-bind="container"
|
||||
x-transition>
|
||||
<f:for each="{submenu}" as="submenuitem">
|
||||
<f:if condition="{submenuitem.doktype} == 3">
|
||||
<f:then>
|
||||
<li>
|
||||
<a class="text-white" href="{submenuitem.url}" target="_blank" title="{submenuitem.linktext}">{submenuitem.title}</a>
|
||||
</li>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<li>
|
||||
<a class="text-white" href="{f:uri.typolink(parameter: submenuitem.uid)}" title="{submenuitem.linktext}">{submenuitem.title}</a>
|
||||
</li>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</ul>
|
||||
</v:menu>
|
||||
</div>
|
||||
</f:for>
|
||||
</v:menu>
|
||||
</div>
|
||||
<div class="hidden md:block container border-t border-white pt-4">
|
||||
<div class="grid grid-cols-2 gap-16">
|
||||
<div class="flex">
|
||||
<div class="flex items-start space-x-2 pr-4">
|
||||
<svg class="w-12 h-12 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-white">
|
||||
<span class="block uppercase text-xl">
|
||||
Service-Telefon
|
||||
<div class="bg-ep-primary-80 sbw:bg-sbw-primary-80 uch:bg-uch-primary-80">
|
||||
<f:if condition="{settings.themekey} == 'ep'">
|
||||
<f:render partial="Disrupter/Outlines" section="Top1" arguments="{classes: 'transform -translate-y-2 text-white'}"/>
|
||||
</f:if>
|
||||
<div class="py-4 relative">
|
||||
<div class="container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 pb-4">
|
||||
<v:menu pageUid="{settings.footerNavPid}" levels="2" expandAll="true">
|
||||
<f:for each="{menu}" as="menuheader" iteration="iteration">
|
||||
<div data-controller="collapse"
|
||||
data-collapse-until-value="991">
|
||||
<span class="headline--4 text-white hidden md:block">
|
||||
{menuheader.title}
|
||||
</span>
|
||||
<button class="headline--4 text-white md:hidden"
|
||||
data-action="collapse#toggle">
|
||||
{menuheader.title}
|
||||
</button>
|
||||
<v:menu pageUid="{menuheader.uid}" useShortcutUid="true" as="submenu">
|
||||
<ul class="list-disc text-white mb-0 pl-4"
|
||||
data-collapse-target="container">
|
||||
<f:for each="{submenu}" as="submenuitem">
|
||||
<f:if condition="{submenuitem.doktype} == 3">
|
||||
<f:then>
|
||||
<li>
|
||||
<a class="text-white" href="{submenuitem.url}" target="_blank" title="{submenuitem.linktext}">{submenuitem.title}</a>
|
||||
</li>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<li>
|
||||
<a class="text-white" href="{f:uri.typolink(parameter: submenuitem.uid)}" title="{submenuitem.linktext}">{submenuitem.title}</a>
|
||||
</li>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</ul>
|
||||
</v:menu>
|
||||
</div>
|
||||
</f:for>
|
||||
</v:menu>
|
||||
</div>
|
||||
<div class="hidden md:block container border-t border-white pt-4">
|
||||
<div class="grid grid-cols-2 gap-16">
|
||||
<div class="flex">
|
||||
<div class="flex items-start space-x-2 pr-4">
|
||||
<svg class="w-12 h-12 text-white">
|
||||
<use href="#icon-phone"></use>
|
||||
</svg>
|
||||
<div class="text-white">
|
||||
<span class="block uppercase text-xl">
|
||||
Service-Telefon
|
||||
</span>
|
||||
<a class="text-white whitespace-nowrap"
|
||||
href="tel:{settings.phoneNumberLink}"
|
||||
title="Service Telefon anrufen"
|
||||
rel="nofollow">
|
||||
{settings.phoneNumber}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white flex-1">
|
||||
<span class="block">
|
||||
{settings.businessHoursDays}
|
||||
</span>
|
||||
<span class="block">
|
||||
{settings.businessHoursTime}
|
||||
</span>
|
||||
<a class="text-white whitespace-nowrap"
|
||||
href="tel:{settings.phoneNumberLink}"
|
||||
title="Service Telefon anrufen"
|
||||
rel="nofollow">
|
||||
{settings.phoneNumber}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white flex-1">
|
||||
<span class="block">
|
||||
{settings.businessHoursDays}
|
||||
</span>
|
||||
<span class="block">
|
||||
{settings.businessHoursTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="flex items-start space-x-2 pr-4">
|
||||
<svg class="w-12 h-12 text-white">
|
||||
<use href="#icon-whatsapp"></use>
|
||||
</svg>
|
||||
<div class="text-white">
|
||||
<span class="block uppercase text-xl">
|
||||
WhatsApp
|
||||
</span>
|
||||
<a class="text-white whitespace-nowrap"
|
||||
href="https://api.whatsapp.com/send?phone={settings.whatsAppNumberLink}"
|
||||
title="WhatsApp"
|
||||
target="_blank"
|
||||
rel="nofollow">
|
||||
{settings.whatsApp}
|
||||
</a>
|
||||
<div class="flex">
|
||||
<div class="flex items-start space-x-2 pr-4">
|
||||
<svg class="w-12 h-12 text-white">
|
||||
<use href="#icon-whatsapp"></use>
|
||||
</svg>
|
||||
<div class="text-white">
|
||||
<span class="block uppercase text-xl">
|
||||
WhatsApp
|
||||
</span>
|
||||
<a class="text-white whitespace-nowrap"
|
||||
href="https://api.whatsapp.com/send?phone={settings.whatsAppNumberLink}"
|
||||
title="WhatsApp"
|
||||
target="_blank"
|
||||
rel="nofollow">
|
||||
{settings.whatsApp}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white flex-1">
|
||||
{f:translate(key: 'label.whatsapp_instructions', extensionName: 'ep_theme')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white flex-1">
|
||||
{f:translate(key: 'label.whatsapp_instructions', extensionName: 'ep_theme')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,10 +178,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button x-data="scrollTop"
|
||||
x-bind="trigger"
|
||||
x-cloak
|
||||
class="fixed bottom-0 right-0 mb-16 md:mb-8 mr-4 md:mr-8 z-10 h-10 w-10 flex items-center justify-center rounded bg-ep-secondary-80">
|
||||
<button data-controller="scroll-top"
|
||||
data-action="scroll-top#scroll"
|
||||
class="hidden fixed bottom-0 right-0 mb-16 md:mb-8 mr-4 md:mr-8 z-10 h-10 w-10 flex items-center justify-center rounded bg-ep-secondary-80">
|
||||
<svg class="w-8 h-8 text-white">
|
||||
<use href="#icon-chevron-up"></use>
|
||||
</svg>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers"
|
||||
xmlns:ce="http://typo3.org/ns/TYPO3/CMS/FluidStyledContent/ViewHelpers"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
@@ -9,9 +8,12 @@
|
||||
<f:if condition="{contentElements}">
|
||||
<f:for each="{contentElements}" as="contentElement">
|
||||
<li>
|
||||
<a href="{f:uri.typolink(parameter: data.pid, section: 'c{contentElement.uid}')}"
|
||||
class="btn btn-warning ep-sidebar"
|
||||
@click.prevent="scrollTo('#c{contentElement.uid}')">{contentElement.header}</a>
|
||||
<button class="button w-full"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="c{contentElement.uid}"
|
||||
data-action="scroll-to#scroll">
|
||||
{contentElement.header}
|
||||
</button>
|
||||
</li>
|
||||
</f:for>
|
||||
</f:if>
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
<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">
|
||||
|
||||
<f:form object="{filterSettings}"
|
||||
name="filterSettings"
|
||||
pluginName="Ajax"
|
||||
data="{'search-target': 'form'}">
|
||||
<div class="border rounded p-4">
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.dateFrom} || {filterSettings.dateTo}', then: 'false', else: 'true')}">
|
||||
<label class="cursor-pointer"
|
||||
data-action="click->collapse#toggle">
|
||||
Zeitraum
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<input type="text"
|
||||
data-search-target="dateRangeLabel"
|
||||
class="cursor-pointer focus:outline-none"
|
||||
value="{ep:dateRange(dateFrom: filterSettings.dateFrom, dateTo: filterSettings.dateTo, includeLabel: 0)}"
|
||||
placeholder="von... bis">
|
||||
<f:form.hidden property="dateFrom"
|
||||
data="{'search-target': 'dateFrom'}"/>
|
||||
<f:form.hidden property="dateTo"
|
||||
data="{'search-target': 'dateTo'}"/>
|
||||
<div data-controller="datepicker"
|
||||
<f:if condition="{filterSettings.dateFrom} && {filterSettings.dateTo}">
|
||||
data-datepicker-date-from-value="{filterSettings.dateFrom -> f:format.date(format: 'Y-m-d')}"
|
||||
data-datepicker-date-to-value="{filterSettings.dateTo -> f:format.date(format: 'Y-m-d')}"
|
||||
</f:if>
|
||||
data-datepicker-min-date-value="{f:format.date(date: 'now', format: 'Y-m-d')}">
|
||||
<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">
|
||||
schließen
|
||||
</button>
|
||||
<button class="daterange__button focus:outline-none"
|
||||
data-action="datepicker#reset">
|
||||
zurücksetzen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.countryUids} || {filterSettings.regionUids}', then: 'false', else: 'true')}">
|
||||
<label class="cursor-pointer"
|
||||
data-action="click->collapse#toggle">
|
||||
Reiseziel
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.destinations}" as="destination">
|
||||
<li>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="countryUids"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{destination.country.uid}"/>
|
||||
{destination.country.label}
|
||||
<f:if condition="{destination.regions}">
|
||||
<ul class="pl-4 pt-1">
|
||||
<f:for each="{destination.regions}" as="region">
|
||||
<li>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="regionUids"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{region.uid}"/>
|
||||
{region.label}
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</f:if>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.conceptUids}', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Reisekonzepte
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.concepts}" as="concept">
|
||||
<li>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="conceptUids"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{concept.uid}"/>
|
||||
{concept.name}
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.priceRanges}', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Preis
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.priceRanges}" as="range">
|
||||
<li>
|
||||
<label>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="priceRanges"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{range.uid}"/>
|
||||
{ep:priceRange(min: range.min, max: range.max)}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.pax} > 1', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Personen
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<div class="border rounded">
|
||||
<select name="pax"
|
||||
data-action="search#filter">
|
||||
<f:for each="{filterOptions.pax}" as="option">
|
||||
<option value="<f:format.raw>{option}</f:format.raw>">
|
||||
<f:format.raw>{option}</f:format.raw>
|
||||
</option>
|
||||
</f:for>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:variable name="hotelTypes" value="{
|
||||
1: 'Ferienwohnung',
|
||||
2: 'Sportclub',
|
||||
3: 'Appartment',
|
||||
4: 'Gästehaus',
|
||||
5: 'Pension'
|
||||
}"/>
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.hotelTypes}', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Unterkunft
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.hotelTypes}" as="type">
|
||||
<li>
|
||||
<label>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="hotelTypes"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{type}"/>
|
||||
{hotelTypes.{type}}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.boardTypes}', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Verpflegung
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.boardTypes}" as="type">
|
||||
<f:if condition="{type.label}">
|
||||
<li>
|
||||
<label>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="boardTypes"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{type.uid}"/>
|
||||
{type.label}
|
||||
</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="mb-2 pb-2 border-b"
|
||||
data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: '{filterSettings.roomTypes}', then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Zimmerarten/Apart.
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0 p-0">
|
||||
<f:for each="{filterOptions.roomTypes}" as="type">
|
||||
<li>
|
||||
<label>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="roomTypes"
|
||||
data="{action: 'search#filter'}"
|
||||
value="{type}"/>
|
||||
{roomTypes.{type}}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:if condition="{filterOptions.busAvailable}">
|
||||
<div data-controller="collapse"
|
||||
data-collapse-collapsed-value="{f:if(condition: filterSettings.bus, then: 'false', else: 'true')}">
|
||||
<label data-action="click->collapse#toggle">
|
||||
Anreise
|
||||
</label>
|
||||
<div data-collapse-target="container">
|
||||
<ul class="mb-0 p-0">
|
||||
<li>
|
||||
<label>
|
||||
<f:form.checkbox class="mr-2"
|
||||
property="bus"
|
||||
data="{action: 'search#filter'}"
|
||||
value="1"/>
|
||||
Busanreise
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
|
||||
</div>
|
||||
</f:form>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
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="991">
|
||||
<p class="lg:hidden">
|
||||
<button class="button button--small w-full"
|
||||
data-action="collapse#toggle">
|
||||
<span>Filter ein-/ausblenden</span>
|
||||
</button>
|
||||
</p>
|
||||
<div class="lg:block"
|
||||
data-collapse-target="container">
|
||||
<h3>Filter</h3>
|
||||
<button class="button button--small w-full"
|
||||
data-action="search#reset">
|
||||
Filter zurücksetzen
|
||||
</button>
|
||||
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-3 lg:col-span-2 relative">
|
||||
<f:render partial="Search/Result" arguments="{_all}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,144 @@
|
||||
<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="searchresult__info alert alert-info">
|
||||
Deine Suche
|
||||
<f:if condition="{meta.organic}">
|
||||
<f:then>
|
||||
<f:if condition="{meta.destinationName}">
|
||||
nach <strong>{meta.destinationName}</strong>
|
||||
</f:if>
|
||||
<f:if condition="{meta.dateRange}">
|
||||
<strong>{meta.dateRange}</strong>
|
||||
</f:if>
|
||||
ergab {meta.total} Treffer
|
||||
</f:then>
|
||||
<f:else>
|
||||
ergab leider keine genauen Treffer. Bitte benutze die Filter.
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
<div class="searchresult__content">
|
||||
<f:for each="{searchResult}" as="section">
|
||||
<div id="concept-{section.concept.uid}">
|
||||
<h2 class="headline headline--secondary headline--underlined">
|
||||
{section.concept.name}
|
||||
</h2>
|
||||
<div class="searchresult-section searchresult-section--{section.concept.code}">
|
||||
<f:for each="{section.items}" as="item">
|
||||
<div class="teaserbox teaserbox--landscape teaserbox--searchresult searchresult-item">
|
||||
<div class="teaserbox__image">
|
||||
<img src="{item.country.flag}" class="teaserbox__flag" alt="{item.country.name}"/>
|
||||
<a href="{item.detailPageUri}" rel="nofollow" title="Details & Buchen">
|
||||
<picture class="searchresult-item__image">
|
||||
<source media="(min-width: 1201px)" data-srcset="{item.hotel.images.resized.m.0.url} 260w"/>
|
||||
<source media="(max-width: 1200px)" data-srcset="{item.hotel.images.resized.l.0.url} 280w"/>
|
||||
<source media="(max-width: 768px)" data-srcset="{item.hotel.images.resized.s.0.url} 220w"/>
|
||||
<img class="scale lazyload" data-src="{item.hotel.images.resized.s[0].url}"
|
||||
title="{item.hotel.images.resized.s.0.title}"
|
||||
alt="{item.hotel.images.resized.s.0.alt}">
|
||||
</picture>
|
||||
</a>
|
||||
<div class="concept-badge concept-badge--{section.concept.code}"
|
||||
id="{section.concept.code}">
|
||||
{section.concept.name}
|
||||
</div>
|
||||
</div>
|
||||
<div class="teaserbox__main">
|
||||
<div class="teaserbox__header">
|
||||
<span class="searchresult-item__headline">
|
||||
{item.region.name}
|
||||
<f:if condition="{section.concept.code} == 'evt'">
|
||||
<strong>
|
||||
<span class="block">
|
||||
{item.product.name}
|
||||
</span>
|
||||
</strong>
|
||||
</f:if>
|
||||
</span>
|
||||
<span class="searchresult-item__subline">
|
||||
{item.hotel.name}
|
||||
</span>
|
||||
<f:if condition="{item.hotel.category}">
|
||||
<span class="searchresult-item__category">
|
||||
Kategorie {item.hotel.category}
|
||||
</span>
|
||||
</f:if>
|
||||
</div>
|
||||
<ul class="check-list searchresult-item__checklist">
|
||||
<f:if condition="{item.hotel.fact}">
|
||||
<li>{item.hotel.fact}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.product.fact}">
|
||||
<li>{item.product.fact}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.region.feature}">
|
||||
<f:then>
|
||||
<li>{item.region.feature}</li>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:if condition="{item.region.fact}">
|
||||
<li>{item.region.fact}</li>
|
||||
</f:if>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</ul>
|
||||
<ul class="check-list check-list--inverted searchresult-item__featurelist">
|
||||
<li>Skipass</li>
|
||||
<f:if condition="{item.board}">
|
||||
<li>{item.board}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.product.feature}">
|
||||
<li>{item.product.feature}</li>
|
||||
</f:if>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="teaserbox__aside">
|
||||
<div class="teaserbox__aside__top">
|
||||
<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>
|
||||
<f:if condition="{item.dates -> f:count()} > 0">
|
||||
<f:if condition="{item.daytrip}">
|
||||
<f:else>
|
||||
<a href="{item.detailPageUri}{f:if(condition: referringPageUri, then: '?ref={referringPageUri -> f:format.urlencode()}')}" rel="nofollow" title="Details & Buchen">
|
||||
<span class="searchresult-item__daterange">
|
||||
<i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')}
|
||||
</span>
|
||||
</a>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<hr>
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
</div>
|
||||
<div class="teaserbox__aside__bottom">
|
||||
<span class="searchresult-item__price">ab {item.minPrice} €</span>
|
||||
<a href="{item.detailPageUri}{f:if(condition: referringPageUri, then: '?ref={referringPageUri -> f:format.urlencode()}')}" class="button button--full searchresult-item__button"
|
||||
rel="nofollow" title="Details">
|
||||
Details{f:if(condition: item.hideBookingButton, else: ' & Buchen')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="teaserbox-watchlist" x-data="watchlistToggle('{item.key}')" x-bind="trigger">
|
||||
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
|
||||
<svg class="w-5 h-5">
|
||||
<use x-bind:href="onList() ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
|
||||
</html>
|
||||
+4
-404
@@ -1,419 +1,19 @@
|
||||
<html data-namespace-typo3-fluid="true" lang="en"
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
|
||||
|
||||
<f:layout name="Default"/>
|
||||
|
||||
<f:section name="Main">
|
||||
<div class="grid grid-cols-3 gap-8" x-ref="container">
|
||||
<div class="col-span-3 lg:col-span-1 relative" x-data="collapse(false, 991)">
|
||||
<p class="lg:hidden">
|
||||
<button class="button button--full button--small" role="button" x-bind="trigger">
|
||||
<i class="fa fa-toggle-on"></i> Filter ein-/ausblenden
|
||||
</button>
|
||||
</p>
|
||||
<div class="lg:block"
|
||||
x-bind="container"
|
||||
x-transition
|
||||
x-cloak
|
||||
>
|
||||
<h3>Filter</h3>
|
||||
<button class="button button--sidebar button--small"
|
||||
x-show="filterCount > 0"
|
||||
x-cloak
|
||||
x-on:click.prevent="reset">
|
||||
Filter zurücksetzen
|
||||
</button>
|
||||
<f:render section="FilterPanel" arguments="{_all}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-3 lg:col-span-2 relative">
|
||||
<f:render section="SearchResult" arguments="{_all}"/>
|
||||
</div>
|
||||
</div>
|
||||
<f:render partial="Search/Main" arguments="{_all}"/>
|
||||
</f:section>
|
||||
|
||||
<f:section name="SearchResult">
|
||||
<div class="searchresult__info alert alert-info">
|
||||
Deine Suche
|
||||
<f:if condition="{meta.organic}">
|
||||
<f:then>
|
||||
<f:if condition="{meta.destinationName}">
|
||||
nach <strong>{meta.destinationName}</strong>
|
||||
</f:if>
|
||||
<f:if condition="{meta.dateRange}">
|
||||
<strong>{meta.dateRange}</strong>
|
||||
</f:if>
|
||||
ergab {meta.total} Treffer
|
||||
</f:then>
|
||||
<f:else>
|
||||
ergab leider keine genauen Treffer. Bitte benutze die Filter.
|
||||
</f:else>
|
||||
</f:if>
|
||||
</div>
|
||||
<div class="searchresult__content">
|
||||
<f:for each="{searchResult}" as="section">
|
||||
<div id="concept-{section.concept.uid}">
|
||||
<h2 class="headline headline--secondary headline--underlined">
|
||||
{section.concept.name}
|
||||
</h2>
|
||||
<div class="searchresult-section searchresult-section--{section.concept.code}">
|
||||
<f:for each="{section.items}" as="item">
|
||||
<div class="teaserbox teaserbox--landscape teaserbox--searchresult searchresult-item">
|
||||
<div class="teaserbox__image">
|
||||
<img src="{item.country.flag}" class="teaserbox__flag" alt="{item.country.name}"/>
|
||||
<a href="{item.detailPageUri}" rel="nofollow" title="Details & Buchen">
|
||||
<picture class="searchresult-item__image">
|
||||
<source media="(min-width: 1201px)" data-srcset="{item.hotel.images.resized.m.0.url} 260w"/>
|
||||
<source media="(max-width: 1200px)" data-srcset="{item.hotel.images.resized.l.0.url} 280w"/>
|
||||
<source media="(max-width: 768px)" data-srcset="{item.hotel.images.resized.s.0.url} 220w"/>
|
||||
<img class="scale lazyload" data-src="{item.hotel.images.resized.s[0].url}"
|
||||
title="{item.hotel.images.resized.s.0.title}"
|
||||
alt="{item.hotel.images.resized.s.0.alt}">
|
||||
</picture>
|
||||
</a>
|
||||
<div class="concept-badge concept-badge--{section.concept.code}"
|
||||
id="{section.concept.code}">
|
||||
{section.concept.name}
|
||||
</div>
|
||||
</div>
|
||||
<div class="teaserbox__main">
|
||||
<div class="teaserbox__header">
|
||||
<span class="searchresult-item__headline">
|
||||
{item.region.name}
|
||||
<f:if condition="{section.concept.code} == 'evt'">
|
||||
<strong>
|
||||
<span class="block">
|
||||
{item.product.name}
|
||||
</span>
|
||||
</strong>
|
||||
</f:if>
|
||||
</span>
|
||||
<span class="searchresult-item__subline">
|
||||
{item.hotel.name}
|
||||
</span>
|
||||
<f:if condition="{item.hotel.category}">
|
||||
<span class="searchresult-item__category">
|
||||
Kategorie {item.hotel.category}
|
||||
</span>
|
||||
</f:if>
|
||||
</div>
|
||||
<ul class="check-list searchresult-item__checklist">
|
||||
<f:if condition="{item.hotel.fact}">
|
||||
<li>{item.hotel.fact}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.product.fact}">
|
||||
<li>{item.product.fact}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.region.feature}">
|
||||
<f:then>
|
||||
<li>{item.region.feature}</li>
|
||||
</f:then>
|
||||
<f:else>
|
||||
<f:if condition="{item.region.fact}">
|
||||
<li>{item.region.fact}</li>
|
||||
</f:if>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</ul>
|
||||
<ul class="check-list check-list--inverted searchresult-item__featurelist">
|
||||
<li>Skipass</li>
|
||||
<f:if condition="{item.board}">
|
||||
<li>{item.board}</li>
|
||||
</f:if>
|
||||
<f:if condition="{item.product.feature}">
|
||||
<li>{item.product.feature}</li>
|
||||
</f:if>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="teaserbox__aside">
|
||||
<div class="teaserbox__aside__top">
|
||||
<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>
|
||||
<f:if condition="{item.dates -> f:count()} > 0">
|
||||
<f:if condition="{item.daytrip}">
|
||||
<f:else>
|
||||
<a href="{item.detailPageUri}{f:if(condition: referringPageUri, then: '?ref={referringPageUri -> f:format.urlencode()}')}" rel="nofollow" title="Details & Buchen">
|
||||
<span class="searchresult-item__daterange">
|
||||
<i class="fa fa-arrow-circle-right"></i> {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')}
|
||||
</span>
|
||||
</a>
|
||||
</f:else>
|
||||
</f:if>
|
||||
</f:if>
|
||||
<hr>
|
||||
<span class="searchresult-item__label">
|
||||
{f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')}
|
||||
</span>
|
||||
</div>
|
||||
<div class="teaserbox__aside__bottom">
|
||||
<span class="searchresult-item__price">ab {item.minPrice} €</span>
|
||||
<a href="{item.detailPageUri}{f:if(condition: referringPageUri, then: '?ref={referringPageUri -> f:format.urlencode()}')}" class="button button--full searchresult-item__button"
|
||||
rel="nofollow" title="Details">
|
||||
Details{f:if(condition: item.hideBookingButton, else: ' & Buchen')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="teaserbox-watchlist" x-data="watchlistToggle('{item.key}')" x-bind="trigger">
|
||||
<span data-microtip-position="top" x-bind:aria-label="label" role="tooltip">
|
||||
<svg class="w-5 h-5">
|
||||
<use x-bind:href="onList() ? '#icon-minus-circle' : '#icon-plus-circle'"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
</div>
|
||||
</f:for>
|
||||
</div>
|
||||
<f:render partial="Search/Result" arguments="{_all}"/>
|
||||
</f:section>
|
||||
|
||||
<f:section name="FilterPanel">
|
||||
<div class="border rounded p-4">
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.dateFrom} || {filterSettings.dateTo}">false</f:if>)"
|
||||
x-init='initDatePicker(
|
||||
<f:format.raw>
|
||||
"{filterOptions.dateFrom -> f:format.date(format: 'Y-m-d')}",
|
||||
"{filterOptions.dateTo -> f:format.date(format: 'Y-m-d')}"
|
||||
</f:format.raw>
|
||||
)'>
|
||||
<label x-bind="trigger">
|
||||
Zeitraum
|
||||
</label>
|
||||
<div x-bind="container" class="relative">
|
||||
<input type="text"
|
||||
class="cursor-pointer focus:outline-none"
|
||||
placeholder="von... bis"
|
||||
x-bind:value="dateRangeLabel"
|
||||
x-bind:class="loading && 'cursor-not-allowed'"
|
||||
x-on:click.prevent="show = show === 'dateRange' ? null : 'dateRange'"/>
|
||||
<div class="absolute top-100 left-0 z-20 bg-white p-4 shadow-md" x-show="show === 'dateRange'" x-transition x-cloak>
|
||||
<template x-if="datePresets">
|
||||
<div class="flex items-center justify-between pb-2">
|
||||
<template x-for="preset in datePresets">
|
||||
<a class="daterange__preset" href="#"
|
||||
x-on:click.prevent="selectDateRangePreset(preset)">
|
||||
<i class="fa fa-calendar"></i> <span x-text="preset.label"></span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<input x-ref="picker" type="hidden">
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<button class="daterange__button focus:outline-none" x-on:click.prevent="show = null">
|
||||
<i class="fa fa-times"></i> schließen
|
||||
</button>
|
||||
<button class="daterange__button focus:outline-none" x-on:click.prevent="resetDateRange()">
|
||||
<i class="fa fa-trash-o"></i> zurücksetzen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.countryUids -> f:count()} || {filterSettings.regionUids -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger" class="cursor-pointer">
|
||||
Reiseziel
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.destinations}" as="destination">
|
||||
<li>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.countryUids"
|
||||
value="{destination.country.uid}">
|
||||
{destination.country.label}
|
||||
<f:if condition="{destination.regions -> f:count()}">
|
||||
<ul>
|
||||
<f:for each="{destination.regions}" as="region">
|
||||
<li>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.regionUids"
|
||||
value="{region.uid}">
|
||||
{region.label}
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</f:if>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.conceptUids -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Reisekonzepte
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.concepts}" as="concept">
|
||||
<li>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.conceptUids"
|
||||
value="{concept.uid}">
|
||||
{concept.name}
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.priceRanges -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Preis
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.priceRanges}" as="range">
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.priceRanges"
|
||||
value="<f:format.raw>{range.uid}</f:format.raw>">
|
||||
{ep:priceRange(min: range.min, max: range.max)}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.pax} > 1">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Personen
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<div class="border rounded">
|
||||
<select x-model="filterSettings.pax" x-on:change="refresh">
|
||||
<f:for each="{filterOptions.pax}" as="option">
|
||||
<option value="<f:format.raw>{option}</f:format.raw>">
|
||||
<f:format.raw>{option}</f:format.raw>
|
||||
</option>
|
||||
</f:for>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:variable name="hotelTypes" value="{
|
||||
1: 'Ferienwohnung',
|
||||
2: 'Sportclub',
|
||||
3: 'Appartment',
|
||||
4: 'Gästehaus',
|
||||
5: 'Pension'
|
||||
}"/>
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.hotelTypes -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Unterkunft
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.hotelTypes}" as="type">
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.hotelTypes"
|
||||
value="<f:format.raw>{type}</f:format.raw>">
|
||||
{hotelTypes.{type}}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.boardTypes -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Verpflegung
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<f:for each="{filterOptions.boardTypes}" as="type">
|
||||
<f:if condition="{type.label}">
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.boardTypes"
|
||||
value="<f:format.raw>{type.uid}</f:format.raw>">
|
||||
{type.label}
|
||||
</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="mb-2 pb-2 border-b" x-data="collapse(<f:if condition="{filterSettings.roomTypes -> f:count()}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Zimmerarten/Apart.
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0 p-0">
|
||||
<f:for each="{filterOptions.roomTypes}" as="type">
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model.number="filterSettings.roomTypes"
|
||||
value="<f:format.raw>{type}</f:format.raw>">
|
||||
{roomTypes.{type}}
|
||||
</label>
|
||||
</li>
|
||||
</f:for>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<f:if condition="{filterOptions.busAvailable}">
|
||||
<div x-data="collapse(<f:if condition="{filterSettings.bus}">false</f:if>)">
|
||||
<label x-bind="trigger">
|
||||
Anreise
|
||||
</label>
|
||||
<div x-bind="container" x-transition>
|
||||
<ul class="mb-0 p-0">
|
||||
<li>
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
class="mr-2"
|
||||
x-on:change="refresh"
|
||||
x-model="filterSettings.bus">
|
||||
Busanreise
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</f:if>
|
||||
</div>
|
||||
<f:render partial="Search/FilterPanel" arguments="{_all}"/>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
<div class="container">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<div class="mb-8 lg:hidden">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: city.name, header: city.headline}"/>
|
||||
@@ -26,11 +27,12 @@
|
||||
<div class="col-span-3 lg:col-span-1">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<p class="hidden lg:block">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<f:render partial="Facts" arguments="{facts: city.facts}"/>
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
<div class="container">
|
||||
<f:if condition="{teasers}">
|
||||
<div class="mb-8 lg:hidden">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: concept.name, header: concept.headline}"/>
|
||||
@@ -50,11 +51,12 @@
|
||||
<div class="col-span-3 lg:col-span-1">
|
||||
<f:if condition="{teasers}">
|
||||
<p class="hidden lg:block">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<v:menu pageUid="{settings.conceptsPageUid}" levels="1">
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
<div class="container">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<div class="mb-8 lg:hidden">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: country.name, header: country.headline}"/>
|
||||
@@ -53,11 +54,12 @@
|
||||
<div class="col-span-3 lg:col-span-1">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<p class="hidden lg:block">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<f:render partial="Facts" arguments="{facts: country.facts}"/>
|
||||
|
||||
+5
-5
@@ -13,11 +13,11 @@
|
||||
<f:variable name="latitude" value="{region.0.data.latitude}"/>
|
||||
<f:variable name="longitude" value="{region.0.data.longitude}"/>
|
||||
</f:if>
|
||||
<div class="osm-map"
|
||||
x-data="osmMap"
|
||||
data-lat="{latitude}"
|
||||
data-lng="{longitude}"
|
||||
data-zoom="17"></div>
|
||||
<div class="w-full aspect-w-16 aspect-h-9 mb-8"
|
||||
data-controller="osm"
|
||||
data-osm-lat-value="{latitude}"
|
||||
data-osm-lng-value="{longitude}"
|
||||
data-osm-zoom-value="17"></div>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
|
||||
+5
-4
@@ -10,11 +10,12 @@
|
||||
<f:if condition="{page.content}">
|
||||
<f:for each="{page.content}" as="element">
|
||||
<f:if condition="{element.data.header}">
|
||||
<a href="{page.link}#c{element.data.uid}"
|
||||
class="button w-full"
|
||||
x-data="scrollTo('c{element.data.uid}')">
|
||||
<button class="button w-full"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="c{element.data.uid}"
|
||||
data-action="scroll-to#scroll">
|
||||
{element.data.header}
|
||||
</a>
|
||||
</button>
|
||||
</f:if>
|
||||
</f:for>
|
||||
</f:if>
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
<div class="container">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<div class="mb-8 lg:hidden">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: hotel.name, header: hotel.headline}"/>
|
||||
@@ -25,11 +26,12 @@
|
||||
<div class="col-span-3 md:col-span-1">
|
||||
<f:if condition="{v:content.get(column: 1) -> f:count()} > 0">
|
||||
<p class="hidden lg:block">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<f:render partial="Facts" arguments="{facts: hotel.facts}"/>
|
||||
@@ -40,14 +42,11 @@
|
||||
<div class="bg-gray-200 p-4">
|
||||
<p><strong>Lage</strong></p>
|
||||
<div>
|
||||
<f:format.raw>
|
||||
<div class="osm-map"
|
||||
x-data="osmMap"
|
||||
data-lat="{hotel.latitude}"
|
||||
data-lng="{hotel.longitude}"
|
||||
data-zoom="12"
|
||||
data-link="{f:uri.typolink(parameter: hotel.externalLink)}"></div>
|
||||
</f:format.raw>
|
||||
<div class="w-full aspect-w-16 aspect-h-9"
|
||||
data-controller="osm"
|
||||
data-osm-lat-value="{hotel.latitude}"
|
||||
data-osm-lng-value="{hotel.longitude}"
|
||||
data-osm-external-link-value="{f:uri.typolink(parameter: hotel.externalLink)}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
<f:if condition="{product.bookable}">
|
||||
<h2>Buchungsoptionen</h2>
|
||||
<div class="ajax-content-wrapper"
|
||||
x-data="ajaxContent('{ep:uri.ajax(action: 'pricetableHtml', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'html')}')">
|
||||
<div class="ajax-content" x-ref="container"></div>
|
||||
<div x-ref="trigger">
|
||||
data-controller="ajax-content"
|
||||
data-ajax-content-uri-value="{ep:uri.ajax(action: 'pricetableHtml', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: product, hotel: hotel}', format: 'html')}">
|
||||
<div class="ajax-content" data-ajax-content-target="container"></div>
|
||||
<div data-ajax-content-target="trigger">
|
||||
<img src="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}" alt="Loading" title="Loading">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<f:section name="Main">
|
||||
<ep:container data="{data}">
|
||||
<div class="w-full aspect-w-16 aspect-h-9 mb-8"
|
||||
x-data="osmMap"
|
||||
data-markers='{markerData -> f:format.raw()}'></div>
|
||||
data-controller="osm"
|
||||
data-osm-markers-value='{markerData -> f:format.raw()}'></div>
|
||||
</ep:container>
|
||||
</f:section>
|
||||
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
|
||||
<f:section name="Main">
|
||||
<div class="ajax-content-wrapper"
|
||||
x-data="ajaxContent('{ep:uri.ajax(extensionName: 'epproducts', action: 'eventPricetable', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: productUid}', format: 'html')}')">
|
||||
<div class="ajax-content" x-ref="container"></div>
|
||||
<div x-ref="trigger">
|
||||
data-controller="ajax-content"
|
||||
data-ajax-content-uri-value="{ep:uri.ajax(extensionName: 'epproducts', action: 'eventPricetable', controller: 'AjaxTable', pageUid: settings.defaultAjaxUid, arguments: '{product: productUid}', format: 'html')}">
|
||||
<div class="ajax-content" data-ajax-content-target="container"></div>
|
||||
<div data-ajax-content-target="trigger">
|
||||
<img src="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}" alt="Loading" title="Loading">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,11 @@
|
||||
</f:if>
|
||||
<f:if condition="{product.faqs}">
|
||||
<button class="button button--light col-span-1 md:col-span-2"
|
||||
x-data="scrollTo('faq')">FAQ</button>
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="faq"
|
||||
data-action="scroll-to#scroll">
|
||||
FAQ
|
||||
</button>
|
||||
</f:if>
|
||||
<div class="col-span-2">
|
||||
<p class="mb-4">
|
||||
@@ -184,13 +188,12 @@
|
||||
<p class="font-bold">
|
||||
Lage
|
||||
</p>
|
||||
<div>
|
||||
<div class="osm-map"
|
||||
x-data="osmMap"
|
||||
data-lat="{hotel.latitude}"
|
||||
data-lng="{hotel.longitude}"
|
||||
data-link="{f:uri.typolink(parameter: hotel.externalLink)}"></div>
|
||||
</div>
|
||||
<div class="w-full aspect-w-16 aspect-h-9"
|
||||
data-controller="osm"
|
||||
data-osm-zoom-value="15"
|
||||
data-osm-lat-value="{hotel.latitude}"
|
||||
data-osm-lng-value="{hotel.longitude}"
|
||||
data-osm-external-link-value="{f:uri.typolink(parameter: hotel.externalLink)}"></div>
|
||||
</div>
|
||||
<f:render partial="Region/Webcam" arguments="{region: product.region}"/>
|
||||
<f:render partial="RegionMaps" arguments="{region: product.region}"/>
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
<div class="container">
|
||||
<f:if condition="{teasers}">
|
||||
<div class="mb-8 lg:hidden">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</f:if>
|
||||
<f:render partial="Header/Header" arguments="{layout: 8, subheader: region.name, header: region.headline}"/>
|
||||
@@ -26,11 +27,12 @@
|
||||
<div class="col-span-3 lg:col-span-1">
|
||||
<f:if condition="{teasers}">
|
||||
<p class="hidden lg:block">
|
||||
<a href="#teasers"
|
||||
x-data="scrollTo('teasers')"
|
||||
class="button w-full mb-8">
|
||||
<button class="button w-full mb-8"
|
||||
data-controller="scroll-to"
|
||||
data-scroll-to-target-value="teasers"
|
||||
data-action="scroll-to#scroll">
|
||||
Direkt zu den Angeboten
|
||||
</a>
|
||||
</button>
|
||||
</p>
|
||||
</f:if>
|
||||
<f:if condition="{region.winter}">
|
||||
@@ -44,7 +46,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div x-data='fbpixel({ "content_ids": [ {region.uid -> f:format.raw()} ], "content_name": "{region.nameInternal -> f:format.raw()}", "content_type": "product" })'></div>
|
||||
<div data-controller="fbpixel"
|
||||
data-fbpixel-props-value='{ "content_ids": [ {region.uid -> f:format.raw()} ], "content_name": "{region.nameInternal -> f:format.raw()}", "content_type": "product" }'></div>
|
||||
</f:section>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -35,15 +35,16 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="CollapsibleSimple">
|
||||
<div class="panel panel-default" x-data="collapse">
|
||||
<div class="panel panel-default"
|
||||
data-controller="collapse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" href="#" x-bind="trigger">
|
||||
<button data-action="collapse#toggle">
|
||||
{label}
|
||||
</a>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="hidden" x-bind="container">
|
||||
<div class="hidden" data-collapse-target="container">
|
||||
<div class="panel-body">
|
||||
<f:if condition="{name}">
|
||||
<div class="form-group">
|
||||
@@ -69,15 +70,17 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="CollapsibleHotel">
|
||||
<div class="panel panel-default" x-data="collapse">
|
||||
<div class="panel panel-default"
|
||||
data-controller="collapse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" href="#" data-bind="trigger">
|
||||
<button data-action="collapse#toggle">
|
||||
{label}
|
||||
</a>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="hidden" data-bind="container">
|
||||
<div class="hidden"
|
||||
data-collapse-target="container">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Name</label>
|
||||
@@ -229,15 +232,17 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="CollapsibleCity">
|
||||
<div class="panel panel-default" x-data="collapse">
|
||||
<div class="panel panel-default"
|
||||
data-controller="collapse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" href="#" x-bind="trigger">
|
||||
<button data-action="collapse#toggle">
|
||||
{label}
|
||||
</a>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="hidden" x-bind="container">
|
||||
<div class="hidden"
|
||||
data-collapse-target="container">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Name</label>
|
||||
@@ -257,15 +262,17 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="CollapsibleRegion">
|
||||
<div class="panel panel-default" x-data="collapse">
|
||||
<div class="panel panel-default"
|
||||
data-controller="collapse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" href="#" x-bind="trigger">
|
||||
<button data-action="collapse#toggle">
|
||||
{label}
|
||||
</a>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="hidden" x-bind="container">
|
||||
<div class="hidden"
|
||||
data-collapse-target="container">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Name</label>
|
||||
@@ -309,15 +316,17 @@
|
||||
</f:section>
|
||||
|
||||
<f:section name="CollapsibleImages">
|
||||
<div class="panel panel-default" x-data="collapse">
|
||||
<div class="panel panel-default"
|
||||
data-controller="collapse">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" href="#" x-bind="trigger">
|
||||
<button data-action="collapse#toggle">
|
||||
{label}
|
||||
</a>
|
||||
</button>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="hidden" x-bind="container">
|
||||
<div class="hidden"
|
||||
data-collapse-target="container">
|
||||
<div class="panel-body">
|
||||
<f:for each="{images}" as="image">
|
||||
<a href="{image}" target="_blank">{image}</a><br>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
sticky: <f:if condition="{fixed}"><f:then>true</f:then><f:else>false</f:else></f:if>
|
||||
</f:format.raw>
|
||||
})'>
|
||||
<div class="searchbar {f:if(condition: fixed, then: ' searchbar--sticky')}"{f:if(condition: fixed, else: ' x-data="sticky(650)" x-bind:class="sticky && \'searchbar--sticky\'"')}>
|
||||
<div class="searchbar {f:if(condition: fixed, then: ' searchbar--sticky')}"{f:if(condition: fixed, else: ' data-controller="sticky" data-sticky-offset-value="650" data-sticky-sticky-class="searchbar--sticky"')}>
|
||||
<f:form action="processSearch" controller="Search" method="post" name="searchParams"
|
||||
object="{searchParams}" pluginName="searchresult" extensionName="epproducts"
|
||||
pageUid="{settings.defaultSearchPageUid}" additionalAttributes="{ref: 'form'}">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<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"
|
||||
xmlns:v="http://typo3.org/ns/FluidTYPO3/Vhs/ViewHelpers">
|
||||
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
|
||||
|
||||
<f:layout name="Default"/>
|
||||
|
||||
@@ -13,15 +12,14 @@
|
||||
style="background-image: url({f:uri.image(src: settings.searchPageHeaderImage, width: '1366', height: '160c+100')})"></div>
|
||||
</div>
|
||||
<div class="container relative"
|
||||
x-data='search({
|
||||
<f:format.raw>
|
||||
uri: "{ep:uri.ajax(controller: 'AjaxSearch', action: 'searchresult', pageUid: settings.defaultAjaxUid, format: 'html')}",
|
||||
initialFilterSettings: {filterSettings -> f:format.json()},
|
||||
datePresets: {settings.datePickerPresets -> f:format.json()}
|
||||
</f:format.raw>
|
||||
})'>
|
||||
<div x-ref="container"></div>
|
||||
<div x-show="loading" class="absolute z-20 inset-0 w-full h-fill bg-overlay-white">
|
||||
data-controller="search"
|
||||
data-search-uri-value="{ep:uri.ajax(controller: 'AjaxSearch', action: 'searchresult', pageUid: settings.defaultAjaxUid, format: 'html')}"
|
||||
data-action="datepicker:range-select->search#selectDateRange">
|
||||
<div data-search-target="resultsContainer">
|
||||
<f:render partial="Search/Main" arguments="{_all}"/>
|
||||
</div>
|
||||
<div class="absolute z-20 inset-0 w-full h-fill bg-overlay-white hidden"
|
||||
data-search-target="loadingIndicator">
|
||||
<div class="absolute mt-16 top-0 left-1/2 transform -translate-x-1/2 flex items-center space-x-4 alert alert-info">
|
||||
<f:image src="EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif" alt="Loading" />
|
||||
<span>Suchergebnis wird geladen...</span>
|
||||
|
||||
@@ -77,6 +77,7 @@ module.exports = {
|
||||
'primary': '#82C3C6',
|
||||
'secondary': '#f9c33d',
|
||||
'primary-10': tinycolor('#82C3C6').setAlpha(0.1).toRgbString(),
|
||||
'primary-50': tinycolor('#82C3C6').setAlpha(0.5).toRgbString(),
|
||||
'primary-80': tinycolor('#82C3C6').setAlpha(0.8).toRgbString(),
|
||||
},
|
||||
'uch': {
|
||||
|
||||
Reference in New Issue
Block a user