From 64b9f97fd2ecc333a51e35014eff194a5f26b6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Fromme?= Date: Thu, 22 Jul 2021 17:38:32 +0200 Subject: [PATCH] WIP Migrate search result --- config/sites/ep-reisen/config.yaml | 18 - package-lock.json | 115 ++++-- package.json | 8 +- .../Controller/AjaxFilterpanelController.php | 22 +- .../Controller/AjaxSearchController.php | 16 +- .../Controller/AjaxSearchbarController.php | 15 +- .../Controller/AjaxWatchlistController.php | 12 +- .../Classes/Controller/SearchController.php | 18 +- .../Classes/Domain/Model/FilterSettings.php | 68 +++- .../Classes/Domain/Model/SearchParams.php | 37 +- .../Domain/Repository/DateRepository.php | 10 +- .../Domain/Repository/FaqRepository.php | 2 +- .../Domain/Repository/HotelRepository.php | 10 +- .../Domain/Repository/ProductRepository.php | 14 +- .../Repository/SnowreportRepository.php | 2 +- .../Classes/Service/DateService.php | 72 +--- .../Classes/Service/FilterService.php | 9 +- .../Classes/Service/FilterSettingsEncoder.php | 18 +- .../Classes/Service/SearchResultService.php | 21 +- .../Service/SearchResultUrlService.php | 14 +- .../RequestArgumentTypeConversionTrait.php | 72 +--- .../Classes/Utility/DateUtility.php | 28 +- .../Classes/Utility/TypeConversionUtility.php | 29 ++ .../Configuration/TypoScript/setup.typoscript | 8 +- .../ext/ep_products/ext_localconf.php | 8 +- .../ViewHelpers/DateRangeViewHelper.php | 22 +- .../ViewHelpers/PriceRangeViewHelper.php | 66 ++++ .../Resources/Private/Assets/js/_app.js | 49 ++- .../Private/Assets/js/components/appconfig.js | 2 +- .../Private/Assets/js/components/collapse.js | 2 +- .../Assets/js/components/filter-panel.js | 153 ++++++++ .../Private/Assets/js/components/scroll-to.js | 9 + .../Assets/js/components/search-bar.js | 117 +++--- .../Assets/js/components/search-result.js | 27 ++ .../Private/Assets/js/components/watchlist.js | 22 ++ .../Templates/AjaxFilterpanel/Panel.html | 223 +++++++++++ .../Templates/AjaxSearch/Searchresult.html | 142 +++++++ .../Private/Templates/AjaxWatchlist/List.html | 132 +++++++ .../Private/Templates/Product/Detail.html | 2 +- .../Private/Templates/Product/Detail_Vue.html | 305 --------------- .../Private/Templates/Product/HotelList.html | 84 ++--- .../Private/Templates/Search/Searchbar.html | 75 ++-- .../Private/Templates/Search/Searchbox.html | 158 ++++---- .../Templates/Search/Searchresult.html | 355 +++--------------- .../Private/Templates/Watchlist/List.html | 9 +- 45 files changed, 1403 insertions(+), 1197 deletions(-) create mode 100644 public/typo3conf/ext/ep_products/Classes/Utility/TypeConversionUtility.php create mode 100644 public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceRangeViewHelper.php create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/filter-panel.js create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/scroll-to.js create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-result.js create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/watchlist.js create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxFilterpanel/Panel.html create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxSearch/Searchresult.html create mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxWatchlist/List.html delete mode 100644 public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail_Vue.html diff --git a/config/sites/ep-reisen/config.yaml b/config/sites/ep-reisen/config.yaml index 5f276606..af2a447f 100644 --- a/config/sites/ep-reisen/config.yaml +++ b/config/sites/ep-reisen/config.yaml @@ -149,24 +149,6 @@ routeEnhancers: type: PersistedAliasMapper tableName: tx_epproducts_domain_model_product routeFieldName: path_segment - SearchResultPlugin: - type: Extbase - extension: EpProducts - plugin: searchresult - limitToPages: - - 41 - routes: - - - routePath: '/ergebnis/{filter_settings}' - _controller: 'Search::searchresult' - _arguments: - filter_settings: filterSettings - defaultController: 'Search::searchresult' - requirements: - filter_settings: '[A-Z\d%\-\|;]+' - aspects: - filter_settings: - type: PassthroughMapper StaticResultsPlugin: type: Extbase extension: EpProducts diff --git a/package-lock.json b/package-lock.json index 28a68bf7..b7561867 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@alpinejs/persist": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@alpinejs/persist/-/persist-3.2.2.tgz", + "integrity": "sha512-OY5QDmr46qvmHygzDjjABfGhWcigI2WVOhu1h4ONP5/TstexviRpyzVygkjmLHEVBiE5zdTU6rFsSEQx+7Karg==" + }, "@babel/code-frame": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", @@ -116,17 +121,28 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz", - "integrity": "sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg==", + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.8.tgz", + "integrity": "sha512-bpYvH8zJBWzeqi1o+co8qOrw+EXzQ/0c74gVmY205AWXy9nifHrOg77y+1zwxX5lXE7Icq4sPlSQ4O2kWBrteQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.14.5", "@babel/helper-function-name": "^7.14.5", - "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-member-expression-to-functions": "^7.14.7", "@babel/helper-optimise-call-expression": "^7.14.5", "@babel/helper-replace-supers": "^7.14.5", "@babel/helper-split-export-declaration": "^7.14.5" + }, + "dependencies": { + "@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + } + } } }, "@babel/helper-create-regexp-features-plugin": { @@ -156,9 +172,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "dev": true, "requires": { "ms": "2.1.2" @@ -999,9 +1015,9 @@ } }, "@babel/preset-env": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.7.tgz", - "integrity": "sha512-itOGqCKLsSUl0Y+1nSfhbuuOlTs0MJk2Iv7iSH+XT/mR8U1zRLO7NjWlYXB47yhK4J/7j+HYty/EhFZDYKa/VA==", + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.8.tgz", + "integrity": "sha512-a9aOppDU93oArQ51H+B8M1vH+tayZbuBqzjOhntGetZVa+4tTu5jp+XTwqHGG2lxslqomPYVSjIxQkFwXzgnxg==", "dev": true, "requires": { "@babel/compat-data": "^7.14.7", @@ -1071,7 +1087,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.14.5", "@babel/plugin-transform-unicode-regex": "^7.14.5", "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.5", + "@babel/types": "^7.14.8", "babel-plugin-polyfill-corejs2": "^0.2.2", "babel-plugin-polyfill-corejs3": "^0.2.2", "babel-plugin-polyfill-regenerator": "^0.2.2", @@ -1084,6 +1100,22 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", + "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "dev": true + }, + "@babel/types": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", + "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.8", + "to-fast-properties": "^2.0.0" + } } } }, @@ -1101,9 +1133,9 @@ } }, "@babel/runtime": { - "version": "7.14.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", - "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz", + "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==", "dev": true, "requires": { "regenerator-runtime": "^0.13.4" @@ -1538,17 +1570,17 @@ } }, "@vue/reactivity": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.2.tgz", - "integrity": "sha512-glJzJoN2xE7I2lRvwKM5u1BHRPTd1yc8iaf//Lai/78/uYAvE5DXp5HzWRFOwMlbRvMGJHIQjOqoxj87cDAaag==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", "requires": { - "@vue/shared": "3.1.2" + "@vue/shared": "3.1.5" } }, "@vue/shared": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.2.tgz", - "integrity": "sha512-EmH/poaDWBPJaPILXNI/1fvUbArJQmmTyVCwvvyDYDFnkPoTclAbHRAtyIvqfez7jybTDn077HTNILpxlsoWhg==" + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" }, "@webassemblyjs/ast": { "version": "1.11.0", @@ -1813,9 +1845,9 @@ "dev": true }, "alpinejs": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.2.1.tgz", - "integrity": "sha512-MYYGUmVHNWN27TvgAtUCYXZlfsXoVCrLQT3a0Ak6CQKqf1TCHfQKxmhzJWscW5kPcLlOK2aYmWbV2H+XyfN2FQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.2.2.tgz", + "integrity": "sha512-XkQKikB4tJTLIQuRUeF86CZnvmAKhjGzw5lmMri+7MTQzz77DTetuOqldBWjEgdJ/DOExXuiM57rQwWfBPdMPA==", "requires": { "@vue/reactivity": "^3.0.2" } @@ -2232,6 +2264,12 @@ "requires": { "ms": "2.0.0" } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true } } }, @@ -2729,9 +2767,9 @@ "dev": true }, "core-js-compat": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.1.tgz", - "integrity": "sha512-xGhzYMX6y7oEGQGAJmP2TmtBLvR4nZmRGEcFa3ubHOq5YEp51gGN9AovVa0AoujGZIq+Wm6dISiYyGNfdflYww==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.15.2.tgz", + "integrity": "sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -3487,6 +3525,12 @@ "requires": { "ms": "2.0.0" } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true } } }, @@ -5658,9 +5702,9 @@ } }, "postcss": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz", - "integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz", + "integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==", "dev": true, "requires": { "colorette": "^1.2.2", @@ -6251,18 +6295,17 @@ } } }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", "dev": true }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/package.json b/package.json index 5bdd65db..c09b58bf 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "build": "NODE_ENV=production ./node_modules/.bin/encore production" }, "dependencies": { + "@alpinejs/persist": "^3.2.2", "@splidejs/splide": "^2.4.21", - "alpinejs": "^3.2.1", + "alpinejs": "^3.2.2", "axios": "^0.21.1", "axios-auth-refresh": "^1.0.7", "bootstrap-sass": "^3.4.1", @@ -26,6 +27,7 @@ "microtip": "^0.2.2", "normalize.css": "^7.0.0", "picturefill": "^3.0.3", + "querystringify": "^2.2.0", "slick-carousel": "^1.8.1", "sticky-js": "^1.3.0", "vue": "^2.6.14", @@ -37,14 +39,14 @@ "devDependencies": { "@babel/plugin-proposal-object-rest-spread": "^7.14.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/preset-env": "^7.14.7", + "@babel/preset-env": "^7.14.8", "@symfony/webpack-encore": "^1.5.0", "@tailwindcss/custom-forms": "^0.2.1", "autoprefixer": "^9.4.6", "core-js": "^3.15.2", "file-loader": "^6.2.0", "node-sass": "^4.14.1", - "postcss": "^8.3.5", + "postcss": "^8.3.6", "postcss-loader": "^5.3.0", "sass-loader": "^9.0.3", "sass-mq": "^3.3.2", diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxFilterpanelController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxFilterpanelController.php index 1826d3af..990a39bf 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxFilterpanelController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxFilterpanelController.php @@ -65,6 +65,26 @@ class AjaxFilterpanelController extends ActionController $this->processFilterSettingsArgument($forcedConceptUids); } + /** + * @param FilterSettings $filterSettings + */ + public function panelAction(FilterSettings $filterSettings) + { + $excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']); + $excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']); + + $filterOptions = $this->filterService->getFilterOptions( + $filterSettings, + $excludedConceptUids, + $excludedRegionUids + ); + + $this->view->assignMultiple([ + 'filterOptions' => $filterOptions, + 'filterSettings' => $filterSettings, + ]); + } + /** * @param FilterSettings $filterSettings * @param string $search @@ -82,7 +102,7 @@ class AjaxFilterpanelController extends ActionController $excludedConceptUids, $excludedRegionUids ); - return json_encode($destinations); + return json_encode($destinations, JSON_THROW_ON_ERROR); } } diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchController.php index 96a12d4d..e7ff9bfa 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchController.php @@ -29,7 +29,6 @@ namespace EP\EpProducts\Controller; use EP\EpProducts\Domain\Model\FilterSettings; use EP\EpProducts\Service\FilterService; -use EP\EpProducts\Service\FilterSettingsEncoder; use EP\EpProducts\Service\SearchResultUrlService; use EP\EpProducts\Service\SearchResultService; use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait; @@ -93,7 +92,6 @@ class AjaxSearchController extends ActionController $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); @@ -111,35 +109,27 @@ class AjaxSearchController extends ActionController $organic = false; } - $filterOptions = $this->filterService->getFilterOptions( - $filterSettings, - $excludedConceptUids, - $excludedRegionUids - ); - $options = [ 'defaultDetailPageUid' => $this->settings['defaultDetailPageUid'], ]; $this->searchResultUrlService->process($searchResult, $options); - $encodedFilterSettings = FilterSettingsEncoder::encode($filterSettings); $referringPageUri = $this ->uriBuilder ->reset() ->setTargetPageUid($this->settings['defaultSearchPageUid']) ->uriFor( 'searchresult', - ['filterSettings' => $encodedFilterSettings], + ['filterSettings' => $filterSettings->toArray()], 'Search', null, 'searchresult' ) ; - return json_encode([ - 'data' => $searchResult->getData(), - 'filterOptions' => $filterOptions, + $this->view->assignMultiple([ + 'searchResult' => $searchResult->getData(), 'referringPageUri' => $referringPageUri, 'meta' => [ 'organic' => $organic, diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchbarController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchbarController.php index 11ed0783..d6c15ba4 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchbarController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxSearchbarController.php @@ -29,7 +29,6 @@ namespace EP\EpProducts\Controller; use EP\EpProducts\Domain\Model\FilterSettings; use EP\EpProducts\Domain\Model\SearchParams; -use EP\EpProducts\Service\DateService; use EP\EpProducts\Service\FilterService; use EP\EpProducts\Traits\RequestArgumentTypeConversionTrait; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -64,7 +63,7 @@ class AjaxSearchbarController extends ActionController * @param SearchParams $searchParams * @return string */ - public function optionsAction(SearchParams $searchParams = null) + public function updateAction(SearchParams $searchParams = null) { if ($searchParams === null) { $searchParams = new SearchParams(); @@ -79,16 +78,6 @@ class AjaxSearchbarController extends ActionController return json_encode([ 'filterOptions' => $filterOptions, 'searchParams' => $searchParams, - ]); + ], JSON_THROW_ON_ERROR); } - - /** - * @return string - */ - public function resetAction() - { - // To reset we simply forward to options action with empty search params - $this->forward('options'); - } - } diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php index 973d986b..ccbae858 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/AjaxWatchlistController.php @@ -79,6 +79,14 @@ class AjaxWatchlistController extends ActionController $this->filterService = $filterService; } + public function initializeAction() + { + if ($this->request->hasArgument('watchlistUids')) { + $watchlistUids = json_decode($this->request->getArgument('watchlistUids'), true, 512, JSON_THROW_ON_ERROR); + $this->request->setArgument('watchlistUids', $watchlistUids); + } + } + /** * @param array $watchlistUids * @return string @@ -104,8 +112,8 @@ class AjaxWatchlistController extends ActionController ]; $this->searchResultUrlService->process($watchlist, $options); - return json_encode([ - 'data' => $watchlist->getData(), + $this->view->assignMultiple([ + 'watchlist' => $watchlist->getData(), 'total' => $watchlist->getTotal() ]); } diff --git a/public/typo3conf/ext/ep_products/Classes/Controller/SearchController.php b/public/typo3conf/ext/ep_products/Classes/Controller/SearchController.php index 1f18e7ea..0009cdbb 100644 --- a/public/typo3conf/ext/ep_products/Classes/Controller/SearchController.php +++ b/public/typo3conf/ext/ep_products/Classes/Controller/SearchController.php @@ -27,7 +27,6 @@ namespace EP\EpProducts\Controller; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use EP\EpEvents\Domain\Model\Search; use EP\EpProducts\Domain\Model\FilterSettings; use EP\EpProducts\Domain\Model\SearchParams; use EP\EpProducts\Service\FilterService; @@ -122,7 +121,7 @@ class SearchController extends ActionController ->setCreateAbsoluteUri(true) ->uriFor( 'searchresult', - ['filterSettings' => FilterSettingsEncoder::encode($filterSettings)], + ['filterSettings' => $filterSettings->toArray()], null, null, 'searchresult' @@ -142,9 +141,18 @@ class SearchController extends ActionController */ public function searchresultAction(FilterSettings $filterSettings) { + $excludedConceptUids = GeneralUtility::trimExplode(',', $this->settings['excludedConceptUids']); + $excludedRegionUids = GeneralUtility::trimExplode(',', $this->settings['excludedRegionUids']); + + $filterOptions = $this->filterService->getFilterOptions( + $filterSettings, + $excludedConceptUids, + $excludedRegionUids + ); + $this->view->assignMultiple([ - 'detailPageUid' => $this->settings['detailPageUid'], 'referringPageUid' => $this->settings['defaultSearchPageUid'], + 'filterOptions' => $filterOptions, 'filterSettings' => $filterSettings, ]); } @@ -155,11 +163,13 @@ class SearchController extends ActionController */ public function staticresultAction($encodedFilterSettings) { + $filterSettings = FilterSettingsEncoder::decode($encodedFilterSettings); + $this->forward( 'searchresult', null, null, - ['filterSettings' => $encodedFilterSettings] + ['filterSettings' => $filterSettings] ); } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Model/FilterSettings.php b/public/typo3conf/ext/ep_products/Classes/Domain/Model/FilterSettings.php index 1118fb05..33736469 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Model/FilterSettings.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Model/FilterSettings.php @@ -2,8 +2,6 @@ namespace EP\EpProducts\Domain\Model; -use TYPO3\CMS\Core\Utility\GeneralUtility; - /*************************************************************** * * Copyright notice @@ -29,6 +27,10 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use EP\EpProducts\Utility\TypeConversionUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Request; + class FilterSettings implements \JsonSerializable { @@ -143,10 +145,6 @@ class FilterSettings implements \JsonSerializable */ protected $bus; - /** - * @param \DateTime $dateFrom - * @param \DateTime $dateTo - */ public function __construct(\DateTime $dateFrom = null, \DateTime $dateTo = null) { if ($dateFrom !== null) { @@ -158,15 +156,13 @@ class FilterSettings implements \JsonSerializable } } - /** - * @param SearchParams $searchParams - * @return FilterSettings - */ - public static function fromSearchParams(SearchParams $searchParams) + public static function fromSearchParams(SearchParams $searchParams): self { $filterSettings = new self; $filterSettings->setDateFrom($searchParams->getDateFrom()); $filterSettings->setDateTo($searchParams->getDateTo()); + $filterSettings->setDestinationUid($searchParams->getDestinationUid()); + $filterSettings->setDestinationType($searchParams->getDestinationType()); if ($searchParams->getDestinationType() && $searchParams->getDestinationUid()) { switch ($searchParams->getDestinationType()) { case self::DESTINATION_TYPE_COUNTRY: @@ -186,11 +182,7 @@ class FilterSettings implements \JsonSerializable return $filterSettings; } - /** - * @param array $searchParams - * @return FilterSettings - */ - public static function fromStaticSearchParams(array $searchParams) + public static function fromStaticSearchParams(array $searchParams): self { $filterSettings = new self; if (!empty($searchParams['country'])) { @@ -230,6 +222,39 @@ class FilterSettings implements \JsonSerializable return $filterSettings; } + public static function fromRequest(Request $request): self + { + $requestArgument = $request->getArgument('filterSettings'); + + $filterSettings = is_array($requestArgument) ? + $requestArgument : json_decode($requestArgument, true, 512, JSON_THROW_ON_ERROR); + + if ($requestArgument instanceof FilterSettings) { + $filterSettingsObject = $requestArgument; + } else { + $filterSettings['dateFrom'] = !empty($filterSettings['dateFrom']) ? new \DateTime($filterSettings['dateFrom']) : null; + $filterSettings['dateTo'] = !empty($filterSettings['dateTo']) ? new \DateTime($filterSettings['dateTo']) : null; + + if (!empty($filterSettings['bus'])) { + $filterSettings['bus'] = \in_array($filterSettings['bus'], [ true, 'true', '1' ], true); + } else { + $filterSettings['bus'] = null; + } + + $filterSettingsObject = new FilterSettings(); + + foreach ($filterSettings as $key => $value) { + $setter = 'set' . ucfirst($key); + $value = TypeConversionUtility::convertType($value); + if (method_exists($filterSettingsObject, $setter)) { + $filterSettingsObject->{$setter}($value); + } + } + } + + return $filterSettingsObject; + } + /** * @return int */ @@ -525,10 +550,8 @@ class FilterSettings implements \JsonSerializable return $this->destinationType !== ''; } - /** - * @return array - */ - public function jsonSerialize() + + public function toArray(): array { return [ 'priceRange' => $this->getPriceRange(), @@ -552,4 +575,9 @@ class FilterSettings implements \JsonSerializable ]; } + public function jsonSerialize() + { + return $this->toArray(); + } + } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Model/SearchParams.php b/public/typo3conf/ext/ep_products/Classes/Domain/Model/SearchParams.php index b44158c1..f67824ea 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Model/SearchParams.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Model/SearchParams.php @@ -27,6 +27,9 @@ namespace EP\EpProducts\Domain\Model; * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use EP\EpProducts\Utility\TypeConversionUtility; +use TYPO3\CMS\Extbase\Mvc\Request; + class SearchParams implements \JsonSerializable { /** @@ -77,11 +80,33 @@ class SearchParams implements \JsonSerializable /** * @param int $pageUid */ - public function __construct($pageUid = 0) + public function __construct(int $pageUid = 0) { $this->setPageUid($pageUid); } + public static function fromRequest(Request $request): self + { + $requestArgument = $request->getArgument('searchParams'); + $searchParams = is_array($requestArgument) ? + $requestArgument : json_decode($requestArgument, true, 512, JSON_THROW_ON_ERROR); + + $searchParams['dateFrom'] = !empty($searchParams['dateFrom']) ? new \DateTime($searchParams['dateFrom']) : null; + $searchParams['dateTo'] = !empty($searchParams['dateTo']) ? new \DateTime($searchParams['dateTo']) : null; + + $searchParamsObject = new self; + + foreach ($searchParams as $key => $value) { + $setter = 'set' . ucfirst($key); + $value = TypeConversionUtility::convertType($value); + if (method_exists($searchParamsObject, $setter)) { + $searchParamsObject->{$setter}($value); + } + } + + return $searchParamsObject; + } + /** * @return int */ @@ -226,10 +251,7 @@ class SearchParams implements \JsonSerializable $this->pageUid = $pageUid; } - /** - * @return array - */ - public function jsonSerialize() + public function toArray(): array { return [ 'priceRange' => $this->getPriceRange(), @@ -244,4 +266,9 @@ class SearchParams implements \JsonSerializable ]; } + public function jsonSerialize() + { + return $this->toArray(); + } + } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php index 7ac405b3..332ecc33 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/DateRepository.php @@ -58,7 +58,7 @@ class DateRepository extends AbstractRepository ; } - $dates = $query->execute()->fetch(); + $dates = $query->execute()->fetchAssociative(); if ($dates['dateRangeFrom'] !== null && $dates['dateRangeTo'] !== null) { $dateRangeFrom = \DateTime::createFromFormat('Y-m-d', $dates['dateRangeFrom']); @@ -122,7 +122,7 @@ class DateRepository extends AbstractRepository $this->addFiltersettingsConditions($query, $filterSettings); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -157,7 +157,7 @@ class DateRepository extends AbstractRepository $this->addPaxFilterConditions($query, $filterSettings); $this->addConceptFilterConditions($query, $filterSettings); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -205,7 +205,7 @@ class DateRepository extends AbstractRepository $this->addPaxFilterConditions($query, $filterSettings); $this->addConceptFilterConditions($query, $filterSettings); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -249,7 +249,7 @@ class DateRepository extends AbstractRepository ; } - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/FaqRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/FaqRepository.php index 49f8f7fd..ffd7e7a7 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/FaqRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/FaqRepository.php @@ -53,7 +53,7 @@ class FaqRepository extends AbstractRepository ->add('orderBy', 'FIELD(uid, ' . $uids . ')') ->setParameter('uids', GeneralUtility::trimExplode(',', $uids), Connection::PARAM_INT_ARRAY) ->execute() - ->fetchAll() + ->fetchAllAssociative() ; return $dataMapper->map(Faq::class, $rows); diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/HotelRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/HotelRepository.php index 9ef37627..0f4972d4 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/HotelRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/HotelRepository.php @@ -49,7 +49,7 @@ class HotelRepository extends AbstractRepository ->setMaxResults(1) ; - return $qb->execute()->fetch(); + return $qb->execute()->fetchAssociative(); } /** @@ -76,7 +76,7 @@ class HotelRepository extends AbstractRepository ->setParameter('hotelUid', $hotelUid) ; - return $qb->execute()->fetch(); + return $qb->execute()->fetchAssociative(); } /** @@ -92,7 +92,7 @@ class HotelRepository extends AbstractRepository ->setParameter('productUid', $productUid) ; - return $qb->execute()->fetchAll(); + return $qb->execute()->fetchAllAssociative(); } /** @@ -144,7 +144,7 @@ class HotelRepository extends AbstractRepository ; } - return $qb->execute()->fetchAll(); + return $qb->execute()->fetchAllAssociative(); } /** @@ -214,7 +214,7 @@ class HotelRepository extends AbstractRepository ; } - return $qb->execute()->fetchAll(); + return $qb->execute()->fetchAllAssociative(); } /** diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php index c6a4f993..db99be4c 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/ProductRepository.php @@ -74,7 +74,7 @@ class ProductRepository extends AbstractRepository ->setParameter('hotelUids', $hotelUids, Connection::PARAM_INT_ARRAY) ; - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -127,7 +127,7 @@ class ProductRepository extends AbstractRepository $this->addFiltersettingsConditions($query, $filterSettings); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -195,7 +195,7 @@ class ProductRepository extends AbstractRepository ; } - return $query->execute()->fetchAll(\PDO::FETCH_ASSOC); + return $query->execute()->fetchAllAssociative(\PDO::FETCH_ASSOC); } /** @@ -242,7 +242,7 @@ class ProductRepository extends AbstractRepository $this->addFiltersettingsConditions($query, $filterSettings); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -282,7 +282,7 @@ class ProductRepository extends AbstractRepository $this->addFiltersettingsConditions($query, $filterSettings, true); - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -341,7 +341,7 @@ class ProductRepository extends AbstractRepository ]) ; - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } /** @@ -369,7 +369,7 @@ class ProductRepository extends AbstractRepository ->setParameter('productUid', $product->getUid()) ; - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } } diff --git a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/SnowreportRepository.php b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/SnowreportRepository.php index 59aea18d..bde5d503 100644 --- a/public/typo3conf/ext/ep_products/Classes/Domain/Repository/SnowreportRepository.php +++ b/public/typo3conf/ext/ep_products/Classes/Domain/Repository/SnowreportRepository.php @@ -65,7 +65,7 @@ class SnowreportRepository extends AbstractRepository ; } - return $query->execute()->fetchAll(); + return $query->execute()->fetchAllAssociative(); } } diff --git a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php index e2dddef3..d51fb5f6 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/DateService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/DateService.php @@ -97,7 +97,7 @@ class DateService implements SingletonInterface * @return array * @throws \Doctrine\DBAL\DBALException */ - public function getPriceTable(array $options) + public function getPriceTable(array $options): array { $resolver = new OptionsResolver(); $this->configurePriceTableOptions($resolver); @@ -128,9 +128,6 @@ class DateService implements SingletonInterface ]); } - /** - * @param OptionsResolver $resolver - */ protected function configurePriceTableOptions(OptionsResolver $resolver) { $resolver->setRequired(['product', 'hotel']); @@ -141,11 +138,7 @@ class DateService implements SingletonInterface $resolver->setAllowedTypes('date', Date::class); } - /** - * @param array $options - * @return array - */ - public function getDatesTable(array $options) + public function getDatesTable(array $options): array { $resolver = new OptionsResolver(); $this->configureDatesTableOptions($resolver); @@ -192,16 +185,10 @@ class DateService implements SingletonInterface $resolver->setAllowedTypes('hotel', Hotel::class); } - /** - * @param Product $product - * @param int $maxPax - * @param string $template - * @return array - */ - public function getEventPriceTable(Product $product, $maxPax = 99, $template = 'base') + public function getEventPriceTable(Product $product, int $maxPax = 99, string $template = 'base'): array { - $priceTableData = $this->productRepository->getEventPriceTable($product); + return $this->preprocessEventPriceTable([ 'priceTableData' => $priceTableData, 'maxPax' => $maxPax, @@ -209,20 +196,12 @@ class DateService implements SingletonInterface ]); } - /** - * @param FilterSettings $filterSettings - * @param string $search - * @param array $excludedConceptUids - * @param array $excludedRegionUids - * @return array - * @throws \Doctrine\DBAL\DBALException - */ public function getAutocompleteOptions ( FilterSettings $filterSettings, - $search, - $excludedConceptUids = [], - $excludedRegionUids = [] + string $search, + array $excludedConceptUids = [], + array $excludedRegionUids = [] ) { $destinations = [ @@ -250,10 +229,8 @@ class DateService implements SingletonInterface $search = mb_strtolower(trim($search)); - foreach ($destinations as $type => $dummy) - { - switch ($type) - { + foreach ($destinations as $type => $dummy) { + switch ($type) { case 'hotel': $result = $this->dateRepository->searchHotels($search, $filterSettings); break; @@ -269,8 +246,7 @@ class DateService implements SingletonInterface ); } - foreach ($result as $row) - { + foreach ($result as $row) { $destinations[$type]['options'][] = [ 'uid' => $row['uid'], 'name' => $row['name'], @@ -281,11 +257,7 @@ class DateService implements SingletonInterface return $destinations; } - /** - * @param array $options - * @return array - */ - public function preprocessPriceTable(array $options) + public function preprocessPriceTable(array $options): array { $resolver = new OptionsResolver(); $resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton', 'nonBookableDateUids']); @@ -301,8 +273,7 @@ class DateService implements SingletonInterface $isDayTrip = $resolvedOptions['isDayTrip']; $nonBookableDateUids = $resolvedOptions['nonBookableDateUids']; - foreach ($priceTableData as $row) - { + foreach ($priceTableData as $row) { $isHideBookingButton = $resolvedOptions['isHideBookingButton'] || in_array($row['dateBusProId'], $nonBookableDateUids, false); $dateStart = new \DateTime($row['dateStart']); @@ -315,7 +286,7 @@ class DateService implements SingletonInterface 'paCode' => $paCode, 'isDayTrip' => $isDayTrip, ]); - $optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices'])); + $optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices'], ['allowed_classes' => false])); $priceTable[] = [ 'dateStart' => $dateStart->format('d.m.Y'), 'dateEnd' => $dateEnd->format('d.m.Y'), @@ -360,11 +331,7 @@ class DateService implements SingletonInterface return $processed; } - /** - * @param array $options - * @return array - */ - public function preprocessDatesTable(array $options) + public function preprocessDatesTable(array $options): array { $resolver = new OptionsResolver(); $resolver->setDefined(['paCode', 'isDayTrip', 'isHideBookingButton', 'nonBookableDateUids']); @@ -382,8 +349,7 @@ class DateService implements SingletonInterface $isDayTrip = $resolvedOptions['isDayTrip']; $nonBookableDateUids = $resolvedOptions['nonBookableDateUids']; - foreach ($dateTableData as $row) - { + foreach ($dateTableData as $row) { $isHideBookingButton = $resolvedOptions['isHideBookingButton'] || in_array($row['dateBusProId'], $nonBookableDateUids, false); $dateStart = new \DateTime($row['dateStart']); @@ -421,11 +387,7 @@ class DateService implements SingletonInterface return $dateTable; } - /** - * @param array $options - * @return array - */ - public function preprocessEventPriceTable(array $options) + public function preprocessEventPriceTable(array $options): array { $resolver = new OptionsResolver(); $resolver->setDefined('paCode'); @@ -520,7 +482,7 @@ class DateService implements SingletonInterface return $priceTable; } - public function checkSurcharcheOrDiscount(array $datesTableData) + public function checkSurcharcheOrDiscount(array $datesTableData): array { $data = [ 'surcharge' => false, diff --git a/public/typo3conf/ext/ep_products/Classes/Service/FilterService.php b/public/typo3conf/ext/ep_products/Classes/Service/FilterService.php index 55a55136..ac892c14 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/FilterService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/FilterService.php @@ -198,9 +198,14 @@ class FilterService implements SingletonInterface if (array_key_exists($index, $priceRanges)) { continue; } - list($min, $max) = $priceRange; + [$min, $max] = $priceRange; if ($row['minPrice'] >= $min && $row['minPrice'] < $max) { - $priceRanges[$index] = FilterSettings::$priceRanges[$index]; + $range = FilterSettings::$priceRanges[$index]; + $priceRanges[$index] = [ + 'uid' => $index, + 'min' => $range[0], + 'max' => $range[1], + ]; } } } diff --git a/public/typo3conf/ext/ep_products/Classes/Service/FilterSettingsEncoder.php b/public/typo3conf/ext/ep_products/Classes/Service/FilterSettingsEncoder.php index 2c429bf2..1f9fd3e2 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/FilterSettingsEncoder.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/FilterSettingsEncoder.php @@ -58,17 +58,12 @@ class FilterSettingsEncoder ]; } - /** - * @param FilterSettings $filterSettings - * - * @return string - */ - public static function encode(FilterSettings $filterSettings) + public static function encode(FilterSettings $filterSettings): string { $encoded = []; foreach (static::getMapping() as $property) { - list($name, $type) = $property; + [$name, $type] = $property; $method = 'get' . ucfirst($name); $value = $filterSettings->{$method}(); if ($type === 'array') { @@ -76,19 +71,14 @@ class FilterSettingsEncoder } elseif ($type === 'date') { $encoded[] = ($value !== null) ? $value->format('Y-m-d') : ''; } else { - $encoded[] = preg_replace('/\r|\n/', '', $value); + $encoded[] = preg_replace('/[\r\n]/', '', $value); } } return implode('|', $encoded); } - /** - * @param string $data - * - * @return array - */ - public static function decode($data) + public static function decode(string $data): array { $decoded = []; $values = explode('|', $data); diff --git a/public/typo3conf/ext/ep_products/Classes/Service/SearchResultService.php b/public/typo3conf/ext/ep_products/Classes/Service/SearchResultService.php index ea7703bf..54123951 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/SearchResultService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/SearchResultService.php @@ -66,30 +66,19 @@ class SearchResultService implements SingletonInterface $this->flagIconService = $flagIconService; } - /** - * @param FilterSettings $filterSettings - * @param array $excludedConceptUids - * - * @return SearchResult - */ - public function getResult(FilterSettings $filterSettings, $excludedConceptUids = []) + public function getResult(FilterSettings $filterSettings, array $excludedConceptUids = []): SearchResult { $searchResultData = $this->productRepository->getSearchResult($filterSettings, $excludedConceptUids); + return $this->preprocess($searchResultData); } - /** - * @param array $searchResultData - * - * @return SearchResult - */ - public function preprocess(array $searchResultData) + public function preprocess(array $searchResultData): SearchResult { $data = []; $total = 0; - foreach ($searchResultData as $row) - { + foreach ($searchResultData as $row) { // Concepts if (!array_key_exists($row['conceptUid'], $data)) { $data[$row['conceptUid']] = [ @@ -120,7 +109,7 @@ class SearchResultService implements SingletonInterface 'daytrip' => $row['daytrip'], 'hideBookingButton' => $row['hideBookingButton'], 'lowContingent' => $row['lowContingent'], - 'servicesIncluded' => unserialize($row['servicesIncluded']), + 'servicesIncluded' => unserialize($row['servicesIncluded'], ['allowed_classes' => false]), 'hotel' => [ 'uid' => $row['hotelUid'], 'name' => $row['hotelName'], diff --git a/public/typo3conf/ext/ep_products/Classes/Service/SearchResultUrlService.php b/public/typo3conf/ext/ep_products/Classes/Service/SearchResultUrlService.php index 91039a12..8f962b56 100644 --- a/public/typo3conf/ext/ep_products/Classes/Service/SearchResultUrlService.php +++ b/public/typo3conf/ext/ep_products/Classes/Service/SearchResultUrlService.php @@ -50,7 +50,7 @@ class SearchResultUrlService implements SingletonInterface * @param SearchResult $searchResult * @param array $options */ - public function process(SearchResult $searchResult, array $options) + public function process(SearchResult $searchResult, array $options): void { $optionsResolver = new OptionsResolver(); $optionsResolver->setRequired([ 'defaultDetailPageUid' ]); @@ -60,10 +60,8 @@ class SearchResultUrlService implements SingletonInterface $data = $searchResult->getData(); - foreach ($data as $conceptUid => $conceptData) - { - foreach ($conceptData['items'] as $key => $item) - { + foreach ($data as $conceptUid => $conceptData) { + foreach ($conceptData['items'] as $key => $item) { $detailPageUid = $this->getProductPageUid($item['product']['detailPage'], $defaultDetailPageUid); $arguments = [ 'product' => $item['product']['uid'], @@ -88,15 +86,15 @@ class SearchResultUrlService implements SingletonInterface * @param int $defaultUid * @return int */ - protected function getProductPageUid($uid, $defaultUid) + protected function getProductPageUid($uid, int $defaultUid): int { if (is_numeric($uid)) { - return $uid; + return (int) $uid; } if (strpos($uid, 't3://') !== false) { parse_str(parse_url($uid, PHP_URL_QUERY), $arguments); - return $arguments['uid']; + return (int) $arguments['uid']; } return $defaultUid; diff --git a/public/typo3conf/ext/ep_products/Classes/Traits/RequestArgumentTypeConversionTrait.php b/public/typo3conf/ext/ep_products/Classes/Traits/RequestArgumentTypeConversionTrait.php index 7e7a64b4..2f0b5b05 100644 --- a/public/typo3conf/ext/ep_products/Classes/Traits/RequestArgumentTypeConversionTrait.php +++ b/public/typo3conf/ext/ep_products/Classes/Traits/RequestArgumentTypeConversionTrait.php @@ -30,7 +30,6 @@ namespace EP\EpProducts\Traits; use EP\EpProducts\Domain\Model\FilterSettings; use EP\EpProducts\Domain\Model\SearchParams; use EP\EpProducts\Service\FilterService; -use EP\EpProducts\Service\FilterSettingsEncoder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; @@ -44,21 +43,7 @@ trait RequestArgumentTypeConversionTrait public function processSearchParamsArgument(array $forcedConceptUids = []) { if ($this->request->hasArgument('searchParams')) { - - $searchParams = (array) $this->request->getArgument('searchParams'); - - $searchParams['dateFrom'] = !empty($searchParams['dateFrom']) ? new \DateTime($searchParams['dateFrom']) : null; - $searchParams['dateTo'] = !empty($searchParams['dateTo']) ? new \DateTime($searchParams['dateTo']) : null; - - $searchParamsObject = new SearchParams(); - - foreach ($searchParams as $key => $value) { - $setter = 'set' . ucfirst($key); - $value = static::convertType($value); - if (method_exists($searchParamsObject, $setter)) { - $searchParamsObject->{$setter}($value); - } - } + $searchParamsObject = SearchParams::fromRequest($this->request); if ($forcedConceptUids) { $searchParamsObject->setConceptUids($forcedConceptUids); @@ -83,37 +68,7 @@ trait RequestArgumentTypeConversionTrait if (!$this->request->hasArgument('filterSettings')) { $filterSettingsObject = $filterService->getDefaultFilterSettings($forcedConceptUids); } else { - $filterSettingsRaw = $this->request->getArgument('filterSettings'); - - if (is_array($filterSettingsRaw)) { - $filterSettings = $filterSettingsRaw; - } elseif (is_string($filterSettingsRaw)) { - $filterSettingsString = urldecode($filterSettingsRaw); - $filterSettings = FilterSettingsEncoder::decode($filterSettingsString); - } - - if ($filterSettingsRaw instanceof FilterSettings) { - $filterSettingsObject = $filterSettingsRaw; - } else { - $filterSettings['dateFrom'] = !empty($filterSettings['dateFrom']) ? new \DateTime($filterSettings['dateFrom']) : null; - $filterSettings['dateTo'] = !empty($filterSettings['dateTo']) ? new \DateTime($filterSettings['dateTo']) : null; - - if (!empty($filterSettings['bus'])) { - $filterSettings['bus'] = \in_array($filterSettings['bus'], [ 'true', '1' ], true); - } else { - $filterSettings['bus'] = null; - } - - $filterSettingsObject = new FilterSettings(); - - foreach ($filterSettings as $key => $value) { - $setter = 'set' . ucfirst($key); - $value = static::convertType($value); - if (method_exists($filterSettingsObject, $setter)) { - $filterSettingsObject->{$setter}($value); - } - } - } + $filterSettingsObject = FilterSettings::fromRequest($this->request); if ($forcedConceptUids) { $conceptUids = $filterSettingsObject->getConceptUids(); @@ -127,27 +82,4 @@ trait RequestArgumentTypeConversionTrait $this->request->setArgument('filterSettings', $filterSettingsObject); } - /** - * Converts numeric scalars and numeric elements of arrays - * to integers and string scalars to booleans if applicable - * - * @param mixed $value - * @return mixed - */ - protected static function convertType($value) - { - if (\is_string($value) && (strtolower($value) === 'true' || strtolower($value) === 'false')) { - return strtolower($value) === 'true'; - } - if (\is_numeric($value)) { - return (int) $value; - } - if (\is_array($value)) { - return array_map(function($item) { - return static::convertType($item); - }, $value); - } - return $value; - } - } diff --git a/public/typo3conf/ext/ep_products/Classes/Utility/DateUtility.php b/public/typo3conf/ext/ep_products/Classes/Utility/DateUtility.php index 12b3a396..e3ff9d08 100644 --- a/public/typo3conf/ext/ep_products/Classes/Utility/DateUtility.php +++ b/public/typo3conf/ext/ep_products/Classes/Utility/DateUtility.php @@ -30,20 +30,17 @@ namespace EP\EpProducts\Utility; class DateUtility { - /** - * @param \DateTime $dateFrom - * @param \DateTime $dateTo - * @param bool $includeLabel* - * @param string $format - * - * @return string - */ - public static function formatDateRange(\DateTime $dateFrom = null, \DateTime $dateTo = null, $includeLabel = true, $format = 'd.m.Y') - { + public static function formatDateRange + ( + \DateTime $dateFrom = null, + \DateTime $dateTo = null, + bool $includeLabel = true, + string $format = 'd.m.Y' + ): string { if ($dateFrom === null && $dateTo === null) { return ''; } - $label = (bool) $includeLabel ? 'im Zeitraum ' : ''; + $label = $includeLabel ? 'im Zeitraum ' : ''; if ($dateFrom !== null && $dateTo === null) { return $label . 'ab ' . $dateFrom->format($format); } @@ -53,14 +50,7 @@ class DateUtility return $label . $dateFrom->format($format) . '-' . $dateTo->format($format); } - /** - * @param string|int $fromYear - * @param string|int $fromMonth - * @param int $months - * - * @return array - */ - public static function getDateRange($fromYear = null, $fromMonth = null, $months = 6) + public static function getDateRange($fromYear = null, $fromMonth = null, int $months = 6): array { $now = new \DateTime('now'); $currentMonth = (int) $now->format('m'); diff --git a/public/typo3conf/ext/ep_products/Classes/Utility/TypeConversionUtility.php b/public/typo3conf/ext/ep_products/Classes/Utility/TypeConversionUtility.php new file mode 100644 index 00000000..3025ae53 --- /dev/null +++ b/public/typo3conf/ext/ep_products/Classes/Utility/TypeConversionUtility.php @@ -0,0 +1,29 @@ +registerIcon( [ 'AjaxSearch' => 'searchresult', 'AjaxDate' => 'dates', - 'AjaxFilterpanel' => 'autocomplete', - 'AjaxSearchbar' => 'options,reset', + 'AjaxFilterpanel' => 'panel,autocomplete', + 'AjaxSearchbar' => 'update', 'AjaxContingent' => 'list,rooms', 'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable', 'AjaxCalendar' => 'range,contingents,availableRooms', @@ -382,8 +382,8 @@ $iconRegistry->registerIcon( [ 'AjaxSearch' => 'searchresult', 'AjaxDate' => 'dates', - 'AjaxFilterpanel' => 'autocomplete', - 'AjaxSearchbar' => 'options,reset', + 'AjaxFilterpanel' => 'panel,autocomplete', + 'AjaxSearchbar' => 'update', 'AjaxContingent' => 'list,rooms', 'AjaxTable' => 'pricetable,pricetableHtml,eventPricetable', 'AjaxCalendar' => 'range,contingents,availableRooms', diff --git a/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/DateRangeViewHelper.php b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/DateRangeViewHelper.php index c497ca64..882b6f9b 100644 --- a/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/DateRangeViewHelper.php +++ b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/DateRangeViewHelper.php @@ -40,8 +40,8 @@ class DateRangeViewHelper extends AbstractViewHelper { parent::initializeArguments(); - $this->registerArgument('dateFrom', \DateTime::class, 'Date range start'); - $this->registerArgument('dateTo', \DateTime::class, 'Date range end'); + $this->registerArgument('dateFrom', 'mixed', 'Date range start'); + $this->registerArgument('dateTo', 'mixed', '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'); } @@ -58,11 +58,25 @@ class DateRangeViewHelper extends AbstractViewHelper RenderingContextInterface $renderingContext ) { + $dateFrom = static::ensureDateObject($arguments['dateFrom']); + $dateTo = static::ensureDateObject($arguments['dateTo']); + return DateUtility::formatDateRange( - $arguments['dateFrom'], - $arguments['dateTo'], + $dateFrom, + $dateTo, $arguments['includeLabel'], $arguments['format'] ); } + + private static function ensureDateObject($argument): \DateTime + { + if ($argument instanceof \DateTime) { + $dateTime = $argument; + } else { + $dateTime = new \DateTime($argument); + } + + return $dateTime; + } } diff --git a/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceRangeViewHelper.php b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceRangeViewHelper.php new file mode 100644 index 00000000..c38cede3 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Classes/ViewHelpers/PriceRangeViewHelper.php @@ -0,0 +1,66 @@ +, dreipunktnull + * + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +class PriceRangeViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + public function initializeArguments() + { + parent::initializeArguments(); + + $this->registerArgument('min', 'mixed', 'Price range start'); + $this->registerArgument('max', 'mixed', 'Price range end'); + } + + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return mixed + */ + public static function renderStatic( + array $arguments, + \Closure $renderChildrenClosure, + RenderingContextInterface $renderingContext + ) { + if (1 === (int) $arguments['min']) { + return sprintf('bis %d €', $arguments['max']); + } + if (999 === (int) $arguments['max']) { + return sprintf('ab %d €', $arguments['min']); + } + + return sprintf('%d bis %d €', $arguments['min'], $arguments['max']); + } +} diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js index 9a5533ef..82b4c66c 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/_app.js @@ -15,9 +15,11 @@ 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' @@ -26,6 +28,11 @@ 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 searchResult from './components/search-result' +import filterPanel from './components/filter-panel' + +import defaultFilterSettings from './_filtersettings' +import defaultFilterOptions from './_filteroptions' // Executed on document ready document.addEventListener('DOMContentLoaded', () => { @@ -40,10 +47,14 @@ document.addEventListener('DOMContentLoaded', () => { modal: null, }) 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) @@ -52,6 +63,8 @@ document.addEventListener('DOMContentLoaded', () => { Alpine.data('productDetail', productDetail) Alpine.data('daytripDateSelect', daytripDateSelect) Alpine.data('searchBar', searchBar) + Alpine.data('searchResult', searchResult) + Alpine.data('filterPanel', filterPanel) Alpine.start() Glightbox({ @@ -64,31 +77,31 @@ document.addEventListener('DOMContentLoaded', () => { /// LEGACY CODE FROM HERE /// // Import dependencies -import Vue from 'vue' -import $ from 'jquery' -import dayjs from 'dayjs' -import 'dayjs/locale/de' +// import Vue from 'vue' +// import $ from 'jquery' +// import dayjs from 'dayjs' +// import 'dayjs/locale/de' -global.$ = global.jQuery = $; +// global.$ = global.jQuery = $; -require('bootstrap-sass/assets/javascripts/bootstrap/dropdown') -require('bootstrap-sass/assets/javascripts/bootstrap/modal') -require('bootstrap-sass/assets/javascripts/bootstrap/transition') +// require('bootstrap-sass/assets/javascripts/bootstrap/dropdown') +// require('bootstrap-sass/assets/javascripts/bootstrap/modal') +// require('bootstrap-sass/assets/javascripts/bootstrap/transition') // Import components -import SearchBar from './components/SearchBar.vue' -import SearchBox from './components/SearchBox.vue' +// import SearchBar from './components/SearchBar.vue' +// import SearchBox from './components/SearchBox.vue' //import ProductDetail from './components/ProductDetail.vue' -import Calendar from './components/Calendar.vue' -import EventPricetable from './components/EventPriceTable.vue' -import ContactForm from './components/ContactForm.vue' -import Watchlist from './components/Watchlist.vue' -import HotelList from './components/HotelList.vue' +// import Calendar from './components/Calendar.vue' +// import EventPricetable from './components/EventPriceTable.vue' +// import ContactForm from './components/ContactForm.vue' +// import Watchlist from './components/Watchlist.vue' +// import HotelList from './components/HotelList.vue' // Define date filter for vue templates -Vue.filter('date', (value) => { - return dayjs(value).format('DD.MM.YY'); -}); +// Vue.filter('date', (value) => { +// return dayjs(value).format('DD.MM.YY'); +// }); // Store // import store from './store'; diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/appconfig.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/appconfig.js index 050d4187..5cfb991b 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/appconfig.js +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/appconfig.js @@ -3,5 +3,5 @@ export default () => { if (null === configElement) { return {} } - return JSON.parse(configElement.innerHTML); + return JSON.parse(configElement.innerHTML) } diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/collapse.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/collapse.js index c00faa46..69840811 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/collapse.js +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/collapse.js @@ -21,7 +21,7 @@ export default (isCollapsed = true, until = null, from = null) => ({ trigger: { ['@click.prevent']() { this.collapsed = !this.collapsed - } + }, }, container: { ['x-show']() { diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/filter-panel.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/filter-panel.js new file mode 100644 index 00000000..0e1090c4 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/filter-panel.js @@ -0,0 +1,153 @@ +import axios from 'axios' +import flatpickr from 'flatpickr'; +import { German } from 'flatpickr/dist/l10n/de'; +import defaultFilterSettings from '../_filtersettings' + +export default props => ({ + loading: false, + show: null, + picker: null, + uri: props.uri, + datePresets: props.datePresets, + filterSettings: props.initialFilterSettings, + defaultFilterSettings, + filterCount: 0, + dateRangeSelected: { from: null, to: null }, + dateRangeLabel: null, + init() { + this.updateFilterCount() + this.load() + this.$watch('filterSettings', () => { + this.$dispatch('filter-settings-updated', this.filterSettings) + this.updateFilterCount() + this.updateDateRangeLabel() + this.load() + }) + }, + load() { + this.loading = true + let data = new URLSearchParams({ + 'tx_epproducts_ajax[filterSettings]': JSON.stringify(this.filterSettings) + }) + axios.post(this.uri, data) + .then(response => { + this.$refs.panel.innerHTML = response.data + }) + .finally(() => { + this.loading = false + if (null !== this.filterSettings.dateFrom && null !== this.filterSettings.dateTo) { + this.picker.setDate([ this.filterSettings.dateFrom, this.filterSettings.dateTo ]) + this.dateRangeSelected = { from: new Date(this.filterSettings.dateFrom), to: new Date(this.filterSettings.dateTo) } + this.updateDateRangeLabel() + } + this.$el.scrollIntoView({behavior: 'smooth'}) + }) + }, + reset() { + this.filterSettings = {...this.defaultFilterSettings} + this.dateRangeLabel = null + }, + initDatePicker(minDate, maxDate) { + this.picker = flatpickr(this.$refs.picker, { + mode: 'range', + locale: German, + inline: true, + minDate: minDate, + maxDate: maxDate, + onChange: selectedDates => { + if (selectedDates.length === 2) { + this.dateRangeSelected = { from: selectedDates[0], to: selectedDates[1] } + this.updateFilterSettings({ + dateFrom: flatpickr.formatDate(selectedDates[0], 'Y-m-d'), + dateTo: flatpickr.formatDate(selectedDates[1], 'Y-m-d'), + }) + this.updateDateRangeLabel() + this.show = null + } + } + }) + }, + updateFilterSettings(values) { + let filterSettings = {...this.filterSettings} + this.filterSettings = {...filterSettings, ...values} + }, + selectDateRangePreset(preset) { + let dateFrom = new Date(preset.dateFrom) + let dateTo = new Date(preset.dateTo) + this.dateRangeSelected = { from: dateFrom, to: dateTo } + this.updateFilterSettings({ + dateFrom: flatpickr.formatDate(dateFrom, 'Y-m-d'), + dateTo: flatpickr.formatDate(dateTo, 'Y-m-d'), + }) + this.updateDateRangeLabel() + this.picker.setDate([ dateFrom, dateTo ]) + this.show = null + }, + resetDateRange() { + this.dateRangeSelected = { from: null, to: null } + this.updateFilterSettings({ + dateFrom: null, + dateTo: null, + }) + this.updateDateRangeLabel() + this.show = null + }, + toggleFilterValue(name, value, multiple = false) { + let updates = {} + let newValue = this.filterSettings[name] + if (!multiple) { + newValue = newValue === value ? null : value + } else { + let index = newValue.indexOf(value) + if (index !== -1) { + newValue.splice(index, 1) + } else { + newValue.push(value) + } + } + updates[name] = newValue + this.updateFilterSettings(updates) + }, + toggleDestinationOrConcept(uid, name, type) { + let values = { destinationUid: null, destinationName: null, destinationType: null } + if ('concept' === type) { + values = {...values, conceptUids: []} + } + let currentUid = this.filterSettings.destinationUid + let currentType = this.filterSettings.destinationType + if (currentUid !== uid || currentType !== type) { + values = { destinationUid: uid, destinationName: name, destinationType: type } + if ('concept' === type) { + values = {...values, conceptUids: [uid]} + } + } + this.updateFilterSettings(values) + }, + updateDateRangeLabel() { + let dateRangeLabel = '' + if (null !== this.dateRangeSelected.from && null !== this.dateRangeSelected.to) { + let dateFrom = this.dateRangeSelected.from + let dateTo = this.dateRangeSelected.to + dateRangeLabel = `${flatpickr.formatDate(dateFrom, 'd.m.Y')} - ${flatpickr.formatDate(dateTo, 'd.m.Y')}` + } + this.dateRangeLabel = dateRangeLabel + }, + updateFilterCount() { + let count = 0 + if (this.filterSettings.destinationUid) count++ + if (this.filterSettings.destinationName) count++ + if (this.filterSettings.countryUids.length > 0) count++ + if (this.filterSettings.regionUids.length > 0) count++ + if (this.filterSettings.cityUids.length > 0) count++ + if (this.filterSettings.conceptUids.length > 0) count++ + if (this.filterSettings.hotelTypes.length > 0) count++ + if (this.filterSettings.boardTypes.length > 0) count++ + if (this.filterSettings.roomTypes.length > 0) count++ + if (this.filterSettings.dateFrom !== null) count++ + if (this.filterSettings.dateTo !== null) count++ + if (this.filterSettings.priceRange > 0) count++ + if (this.filterSettings.bus) count++ + if (this.filterSettings.pax > 2) count++ + this.filterCount = count + }, +}) diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/scroll-to.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/scroll-to.js new file mode 100644 index 00000000..9305cd20 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/scroll-to.js @@ -0,0 +1,9 @@ +export default targetId => ({ + targetId, + init() { + this.$el.addEventListener('click', e => { + e.preventDefault() + document.getElementById(this.targetId).scrollIntoView({behavior: 'smooth'}) + }) + } +}) diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-bar.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-bar.js index 79e5673a..a8c59ee5 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-bar.js +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-bar.js @@ -3,9 +3,9 @@ import flatpickr from 'flatpickr' import { German } from 'flatpickr/dist/l10n/de' export default props => ({ + loading: false, picker: null, - optionsUri: props.optionsUri, - resetUri: props.resetUri, + uri: props.uri, searchParams: props.initialSearchParams, filterOptions: props.initialFilterOptions, datePresets: props.datePresets, @@ -22,26 +22,28 @@ export default props => ({ this.searchParams.dateFrom = null this.searchParams.dateTo = null } else { - let dateFrom = flatpickr.formatDate(this.dateRangeSelected.from, 'd.m.Y') - let dateTo = flatpickr.formatDate(this.dateRangeSelected.to, 'd.m.Y') - dateRangeLabel = `${dateFrom} - ${dateTo}` - this.searchParams.dateFrom = dateFrom - this.searchParams.dateTo = dateTo + let dateFrom = this.dateRangeSelected.from + let dateTo = this.dateRangeSelected.to + dateRangeLabel = `${flatpickr.formatDate(dateFrom, 'd.m.Y')} - ${flatpickr.formatDate(dateTo, 'd.m.Y')}` + this.searchParams.dateFrom = flatpickr.formatDate(dateFrom, 'Y-m-d') + this.searchParams.dateTo = flatpickr.formatDate(dateTo, 'Y-m-d') } this.dateRangeLabel = dateRangeLabel - this.loadOptions() + this.load() }) }, - loadOptions() { - const data = { 'tx_epproducts_ajax[searchParams]': this.searchParams } - axios({ - url: this.optionsUri, - method: 'post', - data - }).then(response => { - this.applyOptions(response.data) - this.show = null - }) + load() { + this.loading = true + const data = new FormData() + data.set('tx_epproducts_ajax[searchParams]', JSON.stringify(this.searchParams)) + axios.post(this.uri, data) + .then(response => { + this.applyOptions(response.data) + this.show = null + }) + .finally(() => { + this.loading = false + }) }, applyOptions(json) { this.filterOptions.totalResults = json.totalResults @@ -58,26 +60,7 @@ export default props => ({ }, updateSearchParam(param, value) { this.searchParams[param] = value - this.loadOptions() - }, - updateDestination(destination) { - if (destination.type === 'concept') { - this.searchParams.conceptUid = destination.uid - } else { - this.searchParams.destinationUid = destination.uid - this.searchParams.destinationType = destination.type - } - this.loadOptions() - }, - resetDestination() { - axios({ - url: this.resetUri, - method: 'post' - }).then(response => { - this.applyOptions(response.data) - this.searchParams = response.data.searchParams - this.show = null - }) + this.load() }, selectDateRangePreset(preset) { let dateFrom = new Date(preset.dateFrom) @@ -90,10 +73,31 @@ export default props => ({ this.picker.clear() this.picker.jumpToDate() }, + selectDestination(uid, name, type) { + this.destinationName = name + this.destinationSelected = true + this.conceptSelected = false + this.searchParams.destinationUid = uid + this.searchParams.destinationType = type + this.load() + }, + selectConcept(uid, name) { + this.destinationName = name + this.destinationSelected = false + this.conceptSelected = true + this.searchParams.destinationUid = uid + this.searchParams.destinationType = 'concept' + this.load() + }, + resetDestination() { + this.destinationName = 'beliebig' + this.destinationSelected = false + this.conceptSelected = false + this.load() + }, initDatePicker() { this.picker = flatpickr(this.$refs.picker, { mode: 'range', - dateFormat: 'd.m.Y', locale: German, inline: true, onChange: selectedDates => { @@ -102,8 +106,9 @@ export default props => ({ } } }) - if (this.filterOptions.minDate && this.filterOptions.maxDate) { - this.picker.set('minDate', new Date(this.filterOptions.minDate)) + let minDate = this.filterOptions.minDate ? new Date(this.filterOptions.minDate) : new Date + this.picker.set('minDate', minDate) + if (this.filterOptions.maxDate) { this.picker.set('maxDate', new Date(this.filterOptions.maxDate)) } }, @@ -112,11 +117,11 @@ export default props => ({ return 'beliebig' } let range = this.filterOptions.priceRanges[this.searchParams.priceRange] - return this.formatPriceRange(range) + return this.priceRangeFormatted(range) }, - formatPriceRange(range) { - let min = range[0]; - let max = range[1]; + priceRangeFormatted(range) { + let min = range.min; + let max = range.max; if (min === 1) { return 'bis ' + max + ' €' } @@ -125,26 +130,4 @@ export default props => ({ } return min + ' - ' + max + ' €' }, - selectDestination(uid, name, type) { - this.destinationName = name - this.destinationSelected = true - this.conceptSelected = false - this.searchParams.destinationUid = uid - this.searchParams.destinationType = type - this.loadOptions() - }, - selectConcept(uid, name) { - this.destinationName = name - this.destinationSelected = false - this.conceptSelected = true - this.searchParams.destinationUid = uid - this.searchParams.destinationType = 'concept' - this.loadOptions() - }, - resetDestinationSelect() { - this.destinationName = 'beliebig' - this.destinationSelected = false - this.conceptSelected = false - this.resetDestination() - }, }) diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-result.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-result.js new file mode 100644 index 00000000..aa0e4b85 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/search-result.js @@ -0,0 +1,27 @@ +import axios from 'axios' + +export default props => ({ + loading: false, + uri: props.uri, + filterSettings: props.initialFilterSettings, + init() { + this.load() + }, + onFilterSettingsUpdated(e) { + this.filterSettings = e.detail + this.load() + }, + load() { + this.loading = true + let data = new URLSearchParams({ + 'tx_epproducts_ajax[filterSettings]': JSON.stringify(this.filterSettings) + }) + axios.post(this.uri, data) + .then(response => { + this.$refs.results.innerHTML = response.data + }) + .finally(() => { + this.loading = false + }) + } +}) diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/watchlist.js b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/watchlist.js new file mode 100644 index 00000000..392e75bc --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Assets/js/components/watchlist.js @@ -0,0 +1,22 @@ +import axios from 'axios' + +export default uri => ({ + loading: false, + uri, + init() { + this.load() + }, + load() { + this.loading = true + let data = new URLSearchParams({ + 'tx_epproducts_ajax[watchlistUids]': JSON.stringify(this.$store.watchlist), + }) + axios.post(this.uri, data) + .then(response => { + this.$refs.watchlist.innerHTML = response.data + }) + .finally(() => { + this.loading = false + }) + } +}) diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxFilterpanel/Panel.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxFilterpanel/Panel.html new file mode 100644 index 00000000..21f9a7b9 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxFilterpanel/Panel.html @@ -0,0 +1,223 @@ + + +
+
", + "{filterOptions.dateTo -> f:format.date(format: 'Y-m-d')}" + )'> + +
+ +
+ + +
+ + +
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ + + diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxSearch/Searchresult.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxSearch/Searchresult.html new file mode 100644 index 00000000..92aab859 --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxSearch/Searchresult.html @@ -0,0 +1,142 @@ + + +
+ Deine Suche + + + + nach {meta.destinationName} + + + {meta.dateRange} + + ergab {meta.total} Treffer + + + ergab leider keine genauen Treffer. Bitte benutze die Filter. + + +
+
+ +
+

+ {section.concept.name} +

+
+ +
+
+ {item.country.name} + + + + + + {item.hotel.images.resized.s.0.alt} + + +
+ {section.concept.name} +
+
+
+
+ + {item.region.name} + + + + {item.product.name} + + + + + + {item.hotel.name} + + + + Kategorie {item.hotel.category} + + +
+
    + +
  • {item.hotel.fact}
  • +
    + +
  • {item.product.fact}
  • +
    + + +
  • {item.region.feature}
  • +
    + + +
  • {item.region.fact}
  • +
    +
    +
    +
+
    +
  • Skipass
  • + +
  • {item.board}
  • +
    + +
  • {item.product.feature}
  • +
    +
+
+
+
+ + {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')} + + + {ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)} + + + + + + + {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')} + + + + + +
+ + {f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')} + +
+ +
+ + + + + +
+
+
+
+
+
+ + diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxWatchlist/List.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxWatchlist/List.html new file mode 100644 index 00000000..e02d056f --- /dev/null +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/AjaxWatchlist/List.html @@ -0,0 +1,132 @@ + + + + +
+ Deine Merkliste ist aktuell leer... +
+
+ + +
+

+ {section.concept.name} +

+
+ +
+
+ {item.country.name} + + + + + + {item.hotel.images.resized.s.0.alt} + + +
+ {section.concept.name} +
+
+
+
+ + {item.region.name} + + + + {item.product.name} + + + + + + {item.hotel.name} + + + + Kategorie {item.hotel.category} + + +
+
    + +
  • {item.hotel.fact}
  • +
    + +
  • {item.product.fact}
  • +
    + + +
  • {item.region.feature}
  • +
    + + +
  • {item.region.fact}
  • +
    +
    +
    +
+
    +
  • Skipass
  • + +
  • {item.board}
  • +
    + +
  • {item.product.feature}
  • +
    +
+
+
+
+ + {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Zeitraum', else: 'Termin')} + + + {ep:dateRange(dateFrom: item.minDateStart, dateTo: item.maxDateEnd, includeLabel: 0)} + + + + + + + {item.dates -> f:count()} {f:if(condition: '{item.dates -> f:count()} > 1', then: 'Termine', else: 'Termin')} + + + + + +
+ + {f:if(condition: item.daytrip, then: 'Tageweise buchbar', else: '{ep:nightsOptions(options: item.nights, daytrip: item.daytrip)}')} + +
+ +
+ + + + + +
+
+
+
+
+
+
+ + diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html index 7921614a..e663cd27 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail.html @@ -67,7 +67,7 @@ + x-data="scrollTo('faq')">FAQ
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail_Vue.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail_Vue.html deleted file mode 100644 index 5977bd3f..00000000 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/Detail_Vue.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - - ", - contingents: "{ep:uri.ajax(action: 'list', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}", - rooms: "{ep:uri.ajax(action: 'rooms', controller: 'AjaxContingent', format: 'json', pageUid: settings.defaultAjaxUid)}", - pricetable: "{ep:uri.ajax(action: 'pricetable', controller: 'AjaxTable', arguments: '{product: product, hotel: hotel}', format: 'json', pageUid: settings.defaultAjaxUid)}" - }' - argument-prefix='' - :hotel-first="{f:if(condition: settings.hotelOnTop, then: 'true', else: 'false')}" - :bus-pro-id="{product.busProId -> f:format.raw()}" - name-internal="{product.nameInternal -> f:format.raw()}" - :daytrip="{product.daytrip -> v:variable.convert(type: 'int')}" - date-from="{dateFrom}" - date-to="{dateTo}" - inline-template> -
- -
-
-
-
- -
-
-
-
-
-
- - - - - - - - -
-
-
-
- - - - - - - - - -
-
- - - - - - - -
- -
- -
-
- -
- - -
-

Gruppenrabatt bis zu 100€ möglich

-

Für Gruppen bis 15 Personen könnt ihr hier direkt buchen. - Für Gruppen ab 16 Personen erstellen wir euch ein konkretes Angebot zu günstigen - Gruppenkonditionen zu dieser Reise. Anfrage via Tel. 0221 - 272 276 18 oder mit einer - Kurzanfrage!

-
-
-
- - - - - - - - - - - - - - -
- - -
- - - - -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -

Loading...

- - -
-
-
-
-
-
-
f:format.raw()} ], "content_name": "{product.nameInternal -> f:format.raw()}", "content_type": "product" }')">
- -
- - - - - - Preisberechnung - - -
-

Belegungskalender

- -
- - - - Direkt zum Anfrageformular - - - -
-
- - diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/HotelList.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/HotelList.html index 12253fc7..2fd22065 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/HotelList.html +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Product/HotelList.html @@ -5,54 +5,50 @@ - -
- -
-
-
- -
-
-
-
- {product.teaserLong -> f:format.html()} -
-
-
-
-
Wähle deine Unterkunft
-
+
+ +
+
+
+
-
-
-
- - - - - - - -
- Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei, - eine andere Reise zu finden. Ruf uns an unter {settings.phoneNumber} - oder schreibe an . -
-
-
-
+
+
+ {product.teaserLong -> f:format.html()}
-
- +
+
+
Wähle deine Unterkunft
+
+
+
+
+
+
+ + + + + + + +
+ Die von dir gewählte Reise ist aktuell nicht buchbar, aber wir helfen dir gerne dabei, + eine andere Reise zu finden. Ruf uns an unter {settings.phoneNumber} + oder schreibe an . +
+
+
+
+
+
+
diff --git a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Search/Searchbar.html b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Search/Searchbar.html index afbc9a89..f34c15f2 100644 --- a/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Search/Searchbar.html +++ b/public/typo3conf/ext/ep_theme/Resources/Private/Templates/Search/Searchbar.html @@ -6,13 +6,12 @@