Merge branch 'master' into feature/a11y-improvements

This commit is contained in:
Björn Fromme
2025-06-22 11:03:45 +02:00
64 changed files with 1463 additions and 708 deletions
+4
View File
@@ -0,0 +1,4 @@
services:
web:
external_links:
- "ddev-router:myep-next.ddev.site"
+1
View File
@@ -26,6 +26,7 @@
"helhum/typo3-console": "^6.4", "helhum/typo3-console": "^6.4",
"kigkonsult/icalcreator": "^2.29", "kigkonsult/icalcreator": "^2.29",
"league/csv": "^9.2", "league/csv": "^9.2",
"league/oauth2-client": "^2.8",
"league/period": "^4.9", "league/period": "^4.9",
"lochmueller/staticfilecache": "^12.5", "lochmueller/staticfilecache": "^12.5",
"sjbr/sr-freecap": "^2.6", "sjbr/sr-freecap": "^2.6",
Generated
+119 -50
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "f4b99aa9b07e56210c516195545a1d40", "content-hash": "4f59a07672e06df6976e44c1999be0f2",
"packages": [ "packages": [
{ {
"name": "b13/container", "name": "b13/container",
@@ -465,26 +465,29 @@
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
"version": "1.1.4", "version": "1.1.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/doctrine/deprecations.git", "url": "https://github.com/doctrine/deprecations.git",
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
"reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.1 || ^8.0" "php": "^7.1 || ^8.0"
}, },
"conflict": {
"phpunit/phpunit": "<=7.5 || >=13"
},
"require-dev": { "require-dev": {
"doctrine/coding-standard": "^9 || ^12", "doctrine/coding-standard": "^9 || ^12 || ^13",
"phpstan/phpstan": "1.4.10 || 2.0.3", "phpstan/phpstan": "1.4.10 || 2.1.11",
"phpstan/phpstan-phpunit": "^1.0 || ^2", "phpstan/phpstan-phpunit": "^1.0 || ^2",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
"psr/log": "^1 || ^2 || ^3" "psr/log": "^1 || ^2 || ^3"
}, },
"suggest": { "suggest": {
@@ -504,9 +507,9 @@
"homepage": "https://www.doctrine-project.org/", "homepage": "https://www.doctrine-project.org/",
"support": { "support": {
"issues": "https://github.com/doctrine/deprecations/issues", "issues": "https://github.com/doctrine/deprecations/issues",
"source": "https://github.com/doctrine/deprecations/tree/1.1.4" "source": "https://github.com/doctrine/deprecations/tree/1.1.5"
}, },
"time": "2024-12-07T21:18:45+00:00" "time": "2025-04-07T20:06:18+00:00"
}, },
{ {
"name": "doctrine/event-manager", "name": "doctrine/event-manager",
@@ -1744,6 +1747,71 @@
], ],
"time": "2022-01-04T00:13:07+00:00" "time": "2022-01-04T00:13:07+00:00"
}, },
{
"name": "league/oauth2-client",
"version": "2.8.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth2-client.git",
"reference": "9df2924ca644736c835fc60466a3a60390d334f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
"reference": "9df2924ca644736c835fc60466a3a60390d334f9",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
"php": "^7.1 || >=8.0.0 <8.5.0"
},
"require-dev": {
"mockery/mockery": "^1.3.5",
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.11"
},
"type": "library",
"autoload": {
"psr-4": {
"League\\OAuth2\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Alex Bilbie",
"email": "[email protected]",
"homepage": "http://www.alexbilbie.com",
"role": "Developer"
},
{
"name": "Woody Gilk",
"homepage": "https://github.com/shadowhand",
"role": "Contributor"
}
],
"description": "OAuth 2.0 Client Library",
"keywords": [
"Authentication",
"SSO",
"authorization",
"identity",
"idp",
"oauth",
"oauth2",
"single sign on"
],
"support": {
"issues": "https://github.com/thephpleague/oauth2-client/issues",
"source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
},
"time": "2025-02-26T04:37:30+00:00"
},
{ {
"name": "league/period", "name": "league/period",
"version": "4.12.0", "version": "4.12.0",
@@ -2161,16 +2229,16 @@
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
"version": "5.6.1", "version": "5.6.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62",
"reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2219,9 +2287,9 @@
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"support": { "support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.1" "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2"
}, },
"time": "2024-12-07T09:39:29+00:00" "time": "2025-04-13T19:20:35+00:00"
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
@@ -4487,7 +4555,7 @@
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git", "url": "https://github.com/symfony/polyfill-ctype.git",
@@ -4546,7 +4614,7 @@
"portable" "portable"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4566,7 +4634,7 @@
}, },
{ {
"name": "symfony/polyfill-intl-grapheme", "name": "symfony/polyfill-intl-grapheme",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
@@ -4624,7 +4692,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4644,16 +4712,16 @@
}, },
{ {
"name": "symfony/polyfill-intl-icu", "name": "symfony/polyfill-intl-icu",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-icu.git", "url": "https://github.com/symfony/polyfill-intl-icu.git",
"reference": "d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78" "reference": "763d2a91fea5681509ca01acbc1c5e450d127811"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78", "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/763d2a91fea5681509ca01acbc1c5e450d127811",
"reference": "d80a05e9904d2c2b9b95929f3e4b5d3a8f418d78", "reference": "763d2a91fea5681509ca01acbc1c5e450d127811",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4708,7 +4776,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4724,20 +4792,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-12-21T18:38:29+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4791,7 +4859,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4807,11 +4875,11 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-10T14:38:51+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
@@ -4872,7 +4940,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4892,19 +4960,20 @@
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-iconv": "*",
"php": ">=7.2" "php": ">=7.2"
}, },
"provide": { "provide": {
@@ -4952,7 +5021,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -4968,7 +5037,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-12-23T08:48:59+00:00"
}, },
{ {
"name": "symfony/polyfill-php72", "name": "symfony/polyfill-php72",
@@ -5037,7 +5106,7 @@
}, },
{ {
"name": "symfony/polyfill-php73", "name": "symfony/polyfill-php73",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php73.git", "url": "https://github.com/symfony/polyfill-php73.git",
@@ -5093,7 +5162,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -5113,16 +5182,16 @@
}, },
{ {
"name": "symfony/polyfill-php80", "name": "symfony/polyfill-php80",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php80.git", "url": "https://github.com/symfony/polyfill-php80.git",
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -5173,7 +5242,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
@@ -5189,11 +5258,11 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2025-01-02T08:10:11+00:00"
}, },
{ {
"name": "symfony/polyfill-php81", "name": "symfony/polyfill-php81",
"version": "v1.31.0", "version": "v1.32.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-php81.git", "url": "https://github.com/symfony/polyfill-php81.git",
@@ -5249,7 +5318,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0"
}, },
"funding": [ "funding": [
{ {
+6 -9
View File
@@ -255,28 +255,25 @@ routeEnhancers:
- 2736 - 2736
routes: routes:
- -
routePath: '/{travel_info}/{travel_date}' routePath: '/{travel_info}'
_controller: 'Travelinfo::index' _controller: 'Travelinfo::travelInfo'
_arguments: _arguments:
travel_info: travelinfo travel_info: travelInfo
travel_date: travelDate
- -
routePath: '/{travel_code}' routePath: '/{travel_code}'
_controller: 'Travelinfo::travelCode' _controller: 'Travelinfo::travelCode'
_arguments: _arguments:
travel_code: travelCode travel_code: travelCode
defaultController: 'Travelinfo::index' defaultController: 'Travelinfo::travelInfo'
requirements:
travel_code: '[A-Z]+\d{6}$'
aspects: aspects:
travel_info: travel_info:
type: PersistedAliasMapper type: PersistedAliasMapper
tableName: tx_epproducts_domain_model_travelinfo tableName: tx_epproducts_domain_model_travelinfo
routeFieldName: path_segment routeFieldName: path_segment
travel_date:
type: PassthroughMapper
travel_code: travel_code:
type: PassthroughMapper type: PassthroughMapper
requirements:
travel_date: '\d{4}\-\d{2}\-\d{2}|\d{2}\d{2}\d{4}'
PageTypeSuffix: PageTypeSuffix:
type: PageType type: PageType
default: / default: /
@@ -28,6 +28,7 @@ namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Dto\GroupsPriceInquiry; use EP\EpProducts\Domain\Model\Dto\GroupsPriceInquiry;
use EP\EpProducts\Domain\Model\GroupsPriceBoard; use EP\EpProducts\Domain\Model\GroupsPriceBoard;
use EP\EpProducts\Domain\Model\GroupsPriceOption;
use EP\EpProducts\Domain\Model\Hotel; use EP\EpProducts\Domain\Model\Hotel;
use EP\EpProducts\Domain\Repository\GroupsPriceOptionRepository; use EP\EpProducts\Domain\Repository\GroupsPriceOptionRepository;
use EP\EpProducts\Service\EmailService; use EP\EpProducts\Service\EmailService;
@@ -69,6 +70,9 @@ class AjaxGroupsPriceController extends ActionController
public function processFormAction(Hotel $hotel, GroupsPriceInquiry $groupsPriceInquiry) public function processFormAction(Hotel $hotel, GroupsPriceInquiry $groupsPriceInquiry)
{ {
$mode = $groupsPriceInquiry->getMode(); $mode = $groupsPriceInquiry->getMode();
$country = $hotel->getCountry()->getCode();
$effectivePax = $groupsPriceInquiry->getEffectivePax();
$nights = $groupsPriceInquiry->getNights();
if (false === in_array($mode, [GroupsPriceInquiry::MODE_BOOKING, GroupsPriceInquiry::MODE_INQUIRY])) { if (false === in_array($mode, [GroupsPriceInquiry::MODE_BOOKING, GroupsPriceInquiry::MODE_INQUIRY])) {
$mode = GroupsPriceInquiry::MODE_BOOKING; $mode = GroupsPriceInquiry::MODE_BOOKING;
@@ -86,10 +90,25 @@ class AjaxGroupsPriceController extends ActionController
'templateName' => 'GroupsPrice', 'templateName' => 'GroupsPrice',
]; ];
$optionPrice = function (GroupsPriceOption $option) use ($country, $effectivePax, $nights) {
$price = 'CH' === $country ? $option->getPriceChf() : $option->getPrice();
$currency = 'CH' === $country ? 'CHF' : '€';
if (GroupsPriceOption::TYPE_PER_PAX === $option->getType()) {
$price = $price * $effectivePax;
} elseif (GroupsPriceOption::TYPE_PER_NIGHT === $option->getType()) {
$price = $price * $nights;
} elseif (GroupsPriceOption::TYPE_PER_PAX_AND_NIGHT === $option->getType()) {
$price = $price * $nights * $effectivePax;
}
return $price.' '.$currency;
};
try { try {
$options = $this->groupsPriceOptionRepository->findByUids($groupsPriceInquiry->getOptions()); $options = $this->groupsPriceOptionRepository->findByUids($groupsPriceInquiry->getOptions());
$selectedOptions = array_map(function ($option) { $selectedOptions = array_map(function ($option) use ($optionPrice) {
return $option->getTitle(); return $option->getTitle().': '.$optionPrice($option);
}, $options); }, $options);
} }
catch (InvalidQueryException $e) { catch (InvalidQueryException $e) {
@@ -101,13 +120,20 @@ class AjaxGroupsPriceController extends ActionController
'hotel' => $hotel, 'hotel' => $hotel,
'name' => $groupsPriceInquiry->getName(), 'name' => $groupsPriceInquiry->getName(),
'group' => $groupsPriceInquiry->getGroup(), 'group' => $groupsPriceInquiry->getGroup(),
'address' => $groupsPriceInquiry->getAddress(), 'street' => $groupsPriceInquiry->getStreet(),
'postcode' => $groupsPriceInquiry->getPostcode(),
'city' => $groupsPriceInquiry->getCity(),
'email' => $groupsPriceInquiry->getEmail(), 'email' => $groupsPriceInquiry->getEmail(),
'phone' => $groupsPriceInquiry->getPhone(), 'phone' => $groupsPriceInquiry->getPhone(),
'remarks' => $groupsPriceInquiry->getRemarks(), 'remarks' => $groupsPriceInquiry->getRemarks(),
'dateFrom' => $groupsPriceInquiry->getDateFrom(), 'dateFrom' => $groupsPriceInquiry->getDateFrom(),
'dateTo' => $groupsPriceInquiry->getDateTo(), 'dateTo' => $groupsPriceInquiry->getDateTo(),
'nights' => $groupsPriceInquiry->getNights(),
'pax' => $groupsPriceInquiry->getPax(), 'pax' => $groupsPriceInquiry->getPax(),
'children' => $groupsPriceInquiry->getChildren(),
'minors' => $groupsPriceInquiry->getMinors(),
'adolescents' => $groupsPriceInquiry->getAdolescents(),
'adolescentsAge' => $hotel->getGroupsPriceAdolescentsAge(),
'options' => $selectedOptions, 'options' => $selectedOptions,
'board' => $groupsPriceInquiry->getBoard(), 'board' => $groupsPriceInquiry->getBoard(),
'summary' => $groupsPriceInquiry->getSummary(), 'summary' => $groupsPriceInquiry->getSummary(),
@@ -124,8 +150,7 @@ class AjaxGroupsPriceController extends ActionController
protected function errorAction() { protected function errorAction() {
$formErrors = []; $formErrors = [];
if ($this->arguments->validate()->hasErrors()) { if ($this->arguments->validate()->hasErrors()) {
foreach ($this->arguments->validate()->getFlattenedErrors() as $key => $errors) foreach ($this->arguments->validate()->getFlattenedErrors() as $key => $errors) {
{
$parts = explode('.', $key); $parts = explode('.', $key);
$fieldName = array_pop($parts); $fieldName = array_pop($parts);
$errorsRaw = []; $errorsRaw = [];
@@ -52,15 +52,16 @@ class GroupsPriceController extends ActionController
$this->view->assign('hotel', $hotel); $this->view->assign('hotel', $hotel);
$this->view->assign('runningCostsEUR', $this->settings['runningCostsEUR']); $this->view->assign('runningCostsEUR', $this->settings['runningCostsEUR']);
$this->view->assign('runningCostsCHF', $this->settings['runningCostsCHF']); $this->view->assign('runningCostsCHF', $this->settings['runningCostsCHF']);
$this->view->assign('undersubscriptionEUR', $this->settings['undersubscriptionEUR']); $this->view->assign('undersubscription30EUR', $this->settings['undersubscription30EUR']);
$this->view->assign('undersubscriptionCHF', $this->settings['undersubscriptionCHF']); $this->view->assign('undersubscription30CHF', $this->settings['undersubscription30CHF']);
$this->view->assign('undersubscriptionExtEUR', $this->settings['undersubscriptionExtEUR']); $this->view->assign('undersubscription40EUR', $this->settings['undersubscription40EUR']);
$this->view->assign('undersubscriptionExtCHF', $this->settings['undersubscriptionExtCHF']); $this->view->assign('undersubscription40CHF', $this->settings['undersubscription40CHF']);
$this->view->assign('countryCode', $hotel->getCountry()->getCode()); $this->view->assign('countryCode', $hotel->getCountry()->getCode());
$this->view->assign('configs', json_encode(array_values($configs), JSON_HEX_APOS)); $this->view->assign('configs', json_encode(array_values($configs), JSON_HEX_APOS));
$this->view->assign('boards', json_encode($hotel->getGroupsPriceBoards()->toArray(), JSON_HEX_APOS)); $this->view->assign('boards', json_encode($hotel->getGroupsPriceBoards()->toArray(), JSON_HEX_APOS));
$this->view->assign('options', json_encode($hotel->getGroupsPriceOptions()->toArray(), JSON_HEX_APOS)); $this->view->assign('options', json_encode($hotel->getGroupsPriceOptions()->toArray(), JSON_HEX_APOS));
$this->view->assign('mainSeasonFrom', $mainSeasonFrom); $this->view->assign('mainSeasonFrom', $mainSeasonFrom);
$this->view->assign('mainSeasonTo', $mainSeasonTo); $this->view->assign('mainSeasonTo', $mainSeasonTo);
$this->view->assign('adolescentsAge', $hotel->getGroupsPriceAdolescentsAge() ?? 0);
} }
} }
@@ -2,11 +2,10 @@
namespace EP\EpProducts\Controller; namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Date;
use EP\EpProducts\Domain\Model\Travelinfo; use EP\EpProducts\Domain\Model\Travelinfo;
use EP\EpProducts\Domain\Repository\DateRepository;
use EP\EpProducts\Domain\Repository\ProductRepository;
use EP\EpProducts\Domain\Repository\TravelinfoRepository; use EP\EpProducts\Domain\Repository\TravelinfoRepository;
use EP\EpProducts\MyEP\ApiClient;
use EP\EpProducts\MyEP\ApiException;
use TYPO3\CMS\Core\Http\ImmediateResponseException; use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -15,59 +14,34 @@ use TYPO3\CMS\Frontend\Controller\ErrorController;
class TravelinfoController extends ActionController class TravelinfoController extends ActionController
{ {
/** /**
* @var DateRepository * @var ApiClient
*/ */
private $dateRepository; private $apiClient;
/** /**
* @var TravelinfoRepository * @var TravelinfoRepository
*/ */
private $travelinfoRepository; private $travelinfoRepository;
/** public function __construct(ApiClient $apiClient, TravelinfoRepository $travelinfoRepository)
* @var ProductRepository {
*/ $this->apiClient = $apiClient;
private $productRepository;
public function __construct(
DateRepository $dateRepository,
TravelinfoRepository $travelinfoRepository,
ProductRepository $productRepository
) {
$this->dateRepository = $dateRepository;
$this->travelinfoRepository = $travelinfoRepository; $this->travelinfoRepository = $travelinfoRepository;
$this->productRepository = $productRepository;
} }
public function travelCodeAction(string $travelCode) public function travelCodeAction(string $travelCode)
{ {
$travelCode = strtoupper($travelCode); // find specific travel info by code including travel date first
$travelCodeDate = str_replace('-', '/', $travelCode); $travelInfo = $this->travelinfoRepository->findOneByTravelCode($travelCode);
/** @var Date $date */ // find general travel info by code without travel date as fallback
$date = $this->dateRepository->findOneByCode($travelCodeDate); if (null === $travelInfo) {
$travelCodeBase = substr($travelCode, 0, -6);
if (null === $date) { $travelInfo = $this->travelinfoRepository->findOneByTravelCode($travelCodeBase);
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
'Termin nicht gefunden'
);
throw new ImmediateResponseException($response, 1731071856);
} }
$product = $date->getProduct(); // return 404 in case no travel info is available as last resort
if (null === $travelInfo) {
if (null === $product) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
'Produkt nicht gefunden'
);
throw new ImmediateResponseException($response, 1731071856);
}
$travelinfo = $this->travelinfoRepository->findOneByProduct($product);
if (null === $travelinfo) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction( $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'], $GLOBALS['TYPO3_REQUEST'],
'Reiseinformationen nicht gefunden' 'Reiseinformationen nicht gefunden'
@@ -75,22 +49,49 @@ class TravelinfoController extends ActionController
throw new ImmediateResponseException($response, 1731071856); throw new ImmediateResponseException($response, 1731071856);
} }
$this->view->assign('travelinfo', $travelinfo); $this->handleRequest($travelCode, $travelInfo);
$this->view->assign('date', $date);
} }
public function indexAction(Travelinfo $travelinfo, string $travelDate) public function travelInfoAction(Travelinfo $travelInfo)
{ {
$product = $travelinfo->getProduct(); $travelCode = $travelInfo->getTravelCode();
if (preg_match('/^\d{2}\d{2}\d{4}$/', $travelDate)) { $this->handleRequest($travelCode, $travelInfo);
$travelDate = (\DateTimeImmutable::createFromFormat('dmY', $travelDate))->format('Y-m-d');
} }
/** @var Date $date */ private function handleRequest(string $travelCode, Travelinfo $travelInfo)
$date = $this->dateRepository->findForProductAndDate($product, $travelDate)->getFirst(); {
$this->view->assign('travelinfo', $travelinfo); // fetch travel data via API call
$this->view->assign('date', $date); try {
$travelData = $this->apiClient->getTravel($travelCode);
} catch (ApiException $e) {
$travelData = [];
}
// extract courses from additional services
$courses = array_filter($travelData['additionalServices'] ?? [], function ($service) {
return 'KUR' === $service['subType'];
});
// extract board services from additional services
$board = array_filter($travelData['additionalServices'] ?? [], function ($service) {
return 'VPF' === $service['subType'];
});
// extract bus departure time if applicable
$departureDayTime = null;
$service = array_filter($travelData['transportationServices'] ?? [], function ($service) {
return 'BUS' === $service['subType'] && 'RUECK' === $service['direction'];
});
if (null !== $service[0] ?? null) {
$departureDayTime = $service[0]['dayTime'];
}
$this->view->assign('travelInfo', $travelInfo);
$this->view->assign('travelData', $travelData);
$this->view->assign('courses', $courses);
$this->view->assign('board', $board);
$this->view->assign('departureDayTime', $departureDayTime);
} }
} }
@@ -60,7 +60,19 @@ class GroupsPriceInquiry
* @var string * @var string
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty") * @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $address; protected $street;
/**
* @var string
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/
protected $postcode;
/**
* @var string
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/
protected $city;
/** /**
* @var string * @var string
@@ -92,11 +104,31 @@ class GroupsPriceInquiry
*/ */
protected $dateTo; protected $dateTo;
/**
* @var string
*/
protected $nights;
/** /**
* @var string * @var string
*/ */
protected $pax; protected $pax;
/**
* @var string
*/
protected $children;
/**
* @var string
*/
protected $minors;
/**
* @var string
*/
protected $adolescents;
/** /**
* @var array * @var array
*/ */
@@ -104,6 +136,7 @@ class GroupsPriceInquiry
/** /**
* @var GroupsPriceBoard * @var GroupsPriceBoard
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
*/ */
protected $board; protected $board;
@@ -186,21 +219,53 @@ class GroupsPriceInquiry
/** /**
* @return string * @return string
*/ */
public function getAddress() public function getStreet()
{ {
return $this->address; return $this->street;
} }
/** /**
* @param string $address * @param string $street
*/ */
public function setAddress($address) public function setStreet($street)
{ {
$this->address = $address; $this->street = $street;
return $this; return $this;
} }
/**
* @return string
*/
public function getPostcode()
{
return $this->postcode;
}
/**
* @param string $postcode
*/
public function setPostcode($postcode)
{
$this->postcode = $postcode;
}
/**
* @return string
*/
public function getCity()
{
return $this->city;
}
/**
* @param string $city
*/
public function setCity($city)
{
$this->city = $city;
}
/** /**
* @return string * @return string
*/ */
@@ -281,6 +346,22 @@ class GroupsPriceInquiry
$this->dateTo = $dateTo; $this->dateTo = $dateTo;
} }
/**
* @return string
*/
public function getNights()
{
return $this->nights;
}
/**
* @param string $nights
*/
public function setNights($nights)
{
$this->nights = $nights;
}
/** /**
* @return string * @return string
*/ */
@@ -297,6 +378,62 @@ class GroupsPriceInquiry
$this->pax = $pax; $this->pax = $pax;
} }
/**
* @return string
*/
public function getChildren()
{
return $this->children;
}
/**
* @param string $pax
*/
public function setChildren($children)
{
$this->children = $children;
}
/**
* @return string
*/
public function getMinors()
{
return $this->minors;
}
/**
* @param string $minors
*/
public function setMinors($minors)
{
$this->minors = $minors;
}
/**
* @return string
*/
public function getAdolescents()
{
return $this->adolescents;
}
/**
* @param string $adolescents
*/
public function setAdolescents($adolescents)
{
$this->adolescents = $adolescents;
}
/**
* @return int
*/
public function getEffectivePax()
{
return max(30, $this->getPax() - $this->getChildren());
}
/** /**
* @return array * @return array
*/ */
@@ -265,6 +265,16 @@ class Hotel extends AbstractEntity implements TeaserInterface
*/ */
protected $groupsPriceBoards; protected $groupsPriceBoards;
/**
* @var int
*/
protected $groupsPriceAdolescentsAge;
/**
* @var string
*/
protected $groupsPriceTaxLabel;
/** /**
* @var \DateTime * @var \DateTime
*/ */
@@ -1054,6 +1064,26 @@ class Hotel extends AbstractEntity implements TeaserInterface
$this->groupsPriceBoards = $groupsPriceBoards; $this->groupsPriceBoards = $groupsPriceBoards;
} }
public function getGroupsPriceAdolescentsAge()
{
return $this->groupsPriceAdolescentsAge;
}
public function setGroupsPriceAdolescentsAge($groupsPriceAdolescentsAge)
{
$this->groupsPriceAdolescentsAge = $groupsPriceAdolescentsAge;
}
public function getGroupsPriceTaxLabel()
{
return $this->groupsPriceTaxLabel;
}
public function setGroupsPriceTaxLabel($groupsPriceTaxLabel)
{
$this->groupsPriceTaxLabel = $groupsPriceTaxLabel;
}
public function getMainSeasonFrom() public function getMainSeasonFrom()
{ {
return $this->mainSeasonFrom; return $this->mainSeasonFrom;
@@ -191,7 +191,7 @@ class ProductRepository extends AbstractRepository
; ;
} }
public function getPricetable(Product $product, Hotel $hotel, array $filterSettings, Date $date = null, bool $sortByRoom = true): array public function getPricetable(Product $product, Hotel $hotel, array $filterSettings, Date $date = null): array
{ {
$qb = $this->getDbConnection()->createQueryBuilder(); $qb = $this->getDbConnection()->createQueryBuilder();
@@ -207,17 +207,10 @@ class ProductRepository extends AbstractRepository
) )
->from('tx_epproducts_domain_model_date', 'date') ->from('tx_epproducts_domain_model_date', 'date')
->innerJoin('date', 'tx_epproducts_domain_model_room', 'room', 'date.uid = room.date') ->innerJoin('date', 'tx_epproducts_domain_model_room', 'room', 'date.uid = room.date')
->orderBy('room.available', 'DESC') ->orderBy('room.pax', 'ASC')
;
if (true === $sortByRoom) {
$qb->addOrderBy('room.name', 'ASC');
} else {
$qb
->addOrderBy('room.pax', 'ASC')
->addOrderBy('room.price', 'ASC') ->addOrderBy('room.price', 'ASC')
->addOrderBy('room.available', 'DESC')
; ;
}
$qb $qb
->where('date.date_start >= NOW()') ->where('date.date_start >= NOW()')
@@ -365,7 +358,8 @@ class ProductRepository extends AbstractRepository
'date.bus_pro_id as dateBusProId', 'date.hotel_bus_pro_id as hotelBusProId', 'date.bus_pro_id as dateBusProId', 'date.hotel_bus_pro_id as hotelBusProId',
'date.date_end as dateEnd', 'date.hotel as hotelUid', 'date.hotel_name as hotelName', 'date.date_end as dateEnd', 'date.hotel as hotelUid', 'date.hotel_name as hotelName',
'date.hotel_header_title as hotelTitle', 'date.hotel_category as hotelCategory', 'date.hotel_header_title as hotelTitle', 'date.hotel_category as hotelCategory',
'room.pax as roomPax', 'room.price as roomPrice', 'room.available as available', 'room.name as roomName', 'room.pax as roomPax', 'room.price as roomPrice',
'room.available as available',
'hotel.detail_page as hotelDetailPageUid', 'hotel.short_name as hotelShortName', 'hotel.detail_page as hotelDetailPageUid', 'hotel.short_name as hotelShortName',
'hotel.show_as_teaser as hotelShowAsTeaser' 'hotel.show_as_teaser as hotelShowAsTeaser'
) )
@@ -0,0 +1,149 @@
<?php
namespace EP\EpProducts\MyEP;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Token\AccessToken;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ApiClient
{
private static ?AccessToken $accessToken = null;
/**
* @throws ApiException
*/
public function getLastUpdateAt(): ?\DateTimeImmutable
{
$httpClient = $this->getHttpClient();
try {
$response = $httpClient->request('GET', 'last-update');
} catch (TransportExceptionInterface $e) {
throw new ApiException($e->getMessage());
}
try {
if (200 !== $response->getStatusCode()) {
throw new ApiException($response->getStatusCode());
}
$data = $response->toArray(false);
if (null === $data['timestamp'] ?? null) {
return null;
}
return new \DateTimeImmutable($data['timestamp']);
} catch (\Throwable $e) {
throw new ApiException($e->getMessage());
}
}
/**
* @throws ApiException
*/
public function getPickups(): array
{
$httpClient = $this->getHttpClient();
try {
$response = $httpClient->request('GET', 'pickups');
} catch (TransportExceptionInterface $e) {
throw new ApiException($e->getMessage());
}
try {
if (200 !== $response->getStatusCode()) {
throw new ApiException($response->getStatusCode());
}
return $response->toArray(false);
} catch (\Throwable $e) {
throw new ApiException($e->getMessage());
}
}
/**
* @throws ApiException
*/
public function getTravels(): array
{
$httpClient = $this->getHttpClient();
try {
$response = $httpClient->request('GET', 'travels');
} catch (TransportExceptionInterface $e) {
throw new ApiException($e->getMessage());
}
try {
if (200 !== $response->getStatusCode()) {
throw new ApiException($response->getStatusCode());
}
return $response->toArray(false);
} catch (\Throwable $e) {
throw new ApiException($e->getMessage());
}
}
/**
* @throws ApiException
*/
public function getTravel(string $productCode): array
{
$httpClient = $this->getHttpClient();
try {
$response = $httpClient->request('GET', 'travels/' . $productCode);
} catch (TransportExceptionInterface $e) {
throw new ApiException($e->getMessage());
}
try {
if (200 !== $response->getStatusCode()) {
throw new ApiException($response->getStatusCode());
}
return $response->toArray(false);
} catch (\Throwable $e) {
throw new ApiException($e->getMessage());
}
}
private function getHttpClient(): HttpClientInterface
{
$config = GeneralUtility::makeInstance(ExtensionConfiguration::class)
->get('ep_products');
if (null === static::$accessToken || true === static::$accessToken->hasExpired()) {
static::$accessToken = $this
->getProvider($config)
->getAccessToken('client_credentials')
;
}
return HttpClient::createForBaseUri($config['myEpApiBaseUrl'], [
'auth_bearer' => static::$accessToken->getToken(),
]);
}
private function getProvider(array $config): AbstractProvider
{
return new GenericProvider([
'clientId' => $config['myEpApiClientId'],
'clientSecret' => $config['myEpApiClientSecret'],
'redirectUri' => null,
'urlAuthorize' => $config['myEpApiUrlAuthorize'],
'urlAccessToken' => $config['myEpApiUrlToken'],
'urlResourceOwnerDetails' => null,
'scopes' => 'api',
]);
}
}
@@ -0,0 +1,7 @@
<?php
namespace EP\EpProducts\MyEP;
class ApiException extends \Exception
{
}
@@ -109,7 +109,6 @@ class DateService implements SingletonInterface
$date = $resolvedOptions['date']; $date = $resolvedOptions['date'];
$template = $resolvedOptions['template']; $template = $resolvedOptions['template'];
$paCode = $resolvedOptions['paCode']; $paCode = $resolvedOptions['paCode'];
$sortByRoom = $resolvedOptions['sortByRoom'];
if ($filterSettings === null) { if ($filterSettings === null) {
$filterSettings = $this->filterService->getNormalizedFilterSettings(); $filterSettings = $this->filterService->getNormalizedFilterSettings();
@@ -118,7 +117,7 @@ class DateService implements SingletonInterface
$nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true); $nonBookableDateUids = GeneralUtility::trimExplode(',', $product->getNonBookableDates(), true);
return $this->preprocessPriceTable([ return $this->preprocessPriceTable([
'priceTableData' => $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date, $sortByRoom), 'priceTableData' => $this->productRepository->getPricetable($product, $hotel, $filterSettings, $date),
'template' => $template, 'template' => $template,
'paCode' => $paCode, 'paCode' => $paCode,
'isDayTrip' => $product->isDaytrip(), 'isDayTrip' => $product->isDaytrip(),
@@ -188,7 +187,11 @@ class DateService implements SingletonInterface
$resolver->setDefault('isDayTrip', false); $resolver->setDefault('isDayTrip', false);
$resolver->setDefault('showBookingButton', true); $resolver->setDefault('showBookingButton', true);
$resolvedOptions = $resolver->resolve($options); $resolvedOptions = $resolver->resolve($options);
$priceTable = [];
$priceTable = [
'available' => [],
'unavailable' => [],
];
$priceTableData = $resolvedOptions['priceTableData']; $priceTableData = $resolvedOptions['priceTableData'];
$template = $resolvedOptions['template']; $template = $resolvedOptions['template'];
@@ -210,7 +213,10 @@ class DateService implements SingletonInterface
'isDayTrip' => $isDayTrip, 'isDayTrip' => $isDayTrip,
]); ]);
$optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices'], ['allowed_classes' => false])); $optionalServices = $this->preprocessOptionalServices(unserialize($row['roomOptionalServices'], ['allowed_classes' => false]));
$priceTable[] = [
$key = $row['roomAvailable'] && $row['roomPrice'] ? 'available' : 'unavailable';
$priceTable[$key][] = [
'dateStart' => $dateStart->format('d.m.Y'), 'dateStart' => $dateStart->format('d.m.Y'),
'dateEnd' => $dateEnd->format('d.m.Y'), 'dateEnd' => $dateEnd->format('d.m.Y'),
'season' => $row['season'], 'season' => $row['season'],
@@ -378,6 +384,8 @@ class DateService implements SingletonInterface
} }
$entry =& $priceTable[$categoryUid][$hotelUid]['rooms']; $entry =& $priceTable[$categoryUid][$hotelUid]['rooms'];
$isUndersubscription = 1 === preg_match('/mit \d{1,2} Personen/', $row['roomName']);
// Add new entry or replace possible existing entry that is not // Add new entry or replace possible existing entry that is not
// bookable with current record in case of matching category // bookable with current record in case of matching category
// and pax // and pax
@@ -385,7 +393,7 @@ class DateService implements SingletonInterface
$entry[$roomPax] = [ $entry[$roomPax] = [
'price' => $row['roomPrice'], 'price' => $row['roomPrice'],
'available' => $row['available'] && $row['roomPrice'], 'available' => $row['available'] && $row['roomPrice'],
'undersubscription' => $isUndersubscription,
] ; ] ;
} else { } else {
$lastEntry = $entry[$roomPax]; $lastEntry = $entry[$roomPax];
@@ -393,6 +401,7 @@ class DateService implements SingletonInterface
$entry[$roomPax] = [ $entry[$roomPax] = [
'price' => $row['roomPrice'], 'price' => $row['roomPrice'],
'available' => $row['available'] && $row['roomPrice'], 'available' => $row['available'] && $row['roomPrice'],
'undersubscription' => $isUndersubscription,
]; ];
} }
} }
@@ -69,7 +69,7 @@ class SearchResultService implements SingletonInterface
'destinationUid' => null, 'destinationUid' => null,
'destinationType' => null, 'destinationType' => null,
'nights' => null, 'nights' => null,
'pax' => 1, 'pax' => 0,
'pageUid' => null, 'pageUid' => null,
]); ]);
@@ -32,7 +32,7 @@
</config> </config>
</TCEforms> </TCEforms>
</settings.runningCostsCHF> </settings.runningCostsCHF>
<settings.undersubscriptionEUR> <settings.undersubscription30EUR>
<TCEforms> <TCEforms>
<exclude>0</exclude> <exclude>0</exclude>
<label>Unterbelegung 30-39 Personen EUR</label> <label>Unterbelegung 30-39 Personen EUR</label>
@@ -42,8 +42,8 @@
<eval>double2,required</eval> <eval>double2,required</eval>
</config> </config>
</TCEforms> </TCEforms>
</settings.undersubscriptionEUR> </settings.undersubscription30EUR>
<settings.undersubscriptionCHF> <settings.undersubscription30CHF>
<TCEforms> <TCEforms>
<exclude>0</exclude> <exclude>0</exclude>
<label>Unterbelegung 30-39 Personen CHF</label> <label>Unterbelegung 30-39 Personen CHF</label>
@@ -53,8 +53,8 @@
<eval>double2,required</eval> <eval>double2,required</eval>
</config> </config>
</TCEforms> </TCEforms>
</settings.undersubscriptionCHF> </settings.undersubscription30CHF>
<settings.undersubscriptionExtEUR> <settings.undersubscription40EUR>
<TCEforms> <TCEforms>
<exclude>0</exclude> <exclude>0</exclude>
<label>Unterbelegung 40-49 Personen EUR</label> <label>Unterbelegung 40-49 Personen EUR</label>
@@ -64,8 +64,8 @@
<eval>double2,required</eval> <eval>double2,required</eval>
</config> </config>
</TCEforms> </TCEforms>
</settings.undersubscriptionExtEUR> </settings.undersubscription40EUR>
<settings.undersubscriptionExtCHF> <settings.undersubscription40CHF>
<TCEforms> <TCEforms>
<exclude>0</exclude> <exclude>0</exclude>
<label>Unterbelegung 40-49 Personen CHF</label> <label>Unterbelegung 40-49 Personen CHF</label>
@@ -75,7 +75,7 @@
<eval>double2,required</eval> <eval>double2,required</eval>
</config> </config>
</TCEforms> </TCEforms>
</settings.undersubscriptionExtCHF> </settings.undersubscription40CHF>
</el> </el>
</ROOT> </ROOT>
</sDEF> </sDEF>
@@ -44,6 +44,7 @@ return [
['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1], ['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1],
['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0] ['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0]
], ],
'default' => -1,
], ],
], ],
'l10n_parent' => [ 'l10n_parent' => [
@@ -45,6 +45,7 @@ return [
['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1], ['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages', -1],
['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0] ['LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.default_value', 0]
], ],
'default' => -1,
], ],
], ],
'l10n_parent' => [ 'l10n_parent' => [
@@ -30,7 +30,8 @@ return [
--div--;Texte, teaser, description, features, room_types, additional_information, --div--;Texte, teaser, description, features, room_types, additional_information,
--div--;Verknüpfungen, facts, teamer, additional_codes, --div--;Verknüpfungen, facts, teamer, additional_codes,
--div--;Bilder/Dateien, teaser_images, header_images, images, reseller_images, --div--;Bilder/Dateien, teaser_images, header_images, images, reseller_images,
--div--;Gruppenhaus Preise, --palette--;;season, groups_price_configs, groups_price_boards, groups_price_options', --div--;Gruppenhaus Preise, --palette--;;season,--palette--;;others, groups_price_configs,
groups_price_boards, groups_price_options',
], ],
], ],
'palettes' => [ 'palettes' => [
@@ -41,6 +42,7 @@ return [
'name' => ['showitem' => 'name,short_name,--linebreak--,header_title,header_subtitle,--linebreak--,headline'], 'name' => ['showitem' => 'name,short_name,--linebreak--,header_title,header_subtitle,--linebreak--,headline'],
'categories' => ['showitem' => 'type,category'], 'categories' => ['showitem' => 'type,category'],
'season' => ['showitem' => 'main_season_from, main_season_to'], 'season' => ['showitem' => 'main_season_from, main_season_to'],
'others' => ['showitem' => 'season,groups_price_tax_label, groups_price_adolescents_age']
], ],
'columns' => [ 'columns' => [
@@ -639,6 +641,7 @@ return [
'type' => 'inline', 'type' => 'inline',
'foreign_table' => 'tx_epproducts_domain_model_groupspriceconfig', 'foreign_table' => 'tx_epproducts_domain_model_groupspriceconfig',
'foreign_field' => 'hotel', 'foreign_field' => 'hotel',
'foreign_sortby' => 'sorting',
'maxitems' => 99, 'maxitems' => 99,
'minitems' => 0, 'minitems' => 0,
'appearance' => [ 'appearance' => [
@@ -691,6 +694,25 @@ return [
], ],
], ],
], ],
'groups_price_tax_label' => [
'exclude' => 0,
'label' => 'Text Ortstaxe',
'config' => [
'type' => 'input',
'size' => 50,
'eval' => 'trim',
'default' => 'zzgl. vor Ort zu entrichtender Ortstaxe',
],
],
'groups_price_adolescents_age' => [
'exclude' => 0,
'label' => 'Altersgrenze Jugendliche (6-X Jahre)',
'config' => [
'type' => 'input',
'size' => 50,
'eval' => 'int',
],
],
'main_season_from' => [ 'main_season_from' => [
'exclude' => false, 'exclude' => false,
'label' => 'Hauptsaison von', 'label' => 'Hauptsaison von',
@@ -27,7 +27,7 @@ return [
], ],
], ],
'palettes' => [ 'palettes' => [
'basics' => ['showitem' => 'title,path_segment,--linebreak--,product,hide_address,--linebreak--,links, 'basics' => ['showitem' => 'title,path_segment,--linebreak--,travel_code,hide_address,--linebreak--,links,
--linebreak--,teasers'], --linebreak--,teasers'],
'text' => ['showitem' => 'subline,headline,--linebreak--,intro_text,--linebreak--, bus_info_text,--linebreak--, 'text' => ['showitem' => 'subline,headline,--linebreak--,intro_text,--linebreak--, bus_info_text,--linebreak--,
departure_text,--linebreak--,footer_text,--linebreak--,self_arranged_text,--linebreak--,additional_info, departure_text,--linebreak--,footer_text,--linebreak--,self_arranged_text,--linebreak--,additional_info,
@@ -191,32 +191,13 @@ return [
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
), ),
], ],
'product' => [ 'travel_code' => [
'exclude' => false, 'exclude' => false,
'label' => 'Produkt', 'label' => 'Reisecode',
'config' => [ 'config' => [
'type' => 'group', 'type' => 'input',
'internal_type' => 'db', 'size' => 30,
'allowed' => 'tx_epproducts_domain_model_product', 'eval' => 'trim,required'
'minitems' => 1,
'maxitems' => 1,
'size' => 1,
'fieldControl' => [
'elementBrowser' => [
'disabled' => true,
],
],
'fieldWizard' => [
'recordsOverview' => [
'disabled' => true,
],
],
'suggestOptions' => [
'default' => [
'additionalSearchFields' => 'code,name,keywords',
'orderBy' => 'code',
],
],
], ],
], ],
'hide_address' => [ 'hide_address' => [
@@ -1,3 +1,18 @@
# customsubcategory=100=General # cat=General; type=int; label=Searchresult PID
# cat=epproducts/100/100; type=int; label=Searchresult PID
searchPageUid = 0 searchPageUid = 0
# cat=MyEpAPI; type=string; label=MyE&P API base URL (trailing slash!)
myEpApiBaseUrl =
# cat=MyEpAPI; type=string; label=MyE&P API client id
myEpApiClientId =
# cat=MyEpAPI; type=string; label=MyE&P API client secret
myEpApiClientSecret =
# cat=MyEpAPI; type=string; label=MyE&P API authorization URL
myEpApiUrlAuthorize =
# cat=MyEpAPI; type=string; label=MyE&P API token URL
myEpApiUrlToken =
@@ -252,7 +252,7 @@ $boot = function () {
'EP.ep_products', 'EP.ep_products',
'travelinfo', 'travelinfo',
[ [
'Travelinfo' => 'index,travelCode', 'Travelinfo' => 'travelInfo,travelCode',
] ]
); );
@@ -255,6 +255,8 @@ CREATE TABLE tx_epproducts_domain_model_hotel
groups_price_configs int(11) unsigned NOT NULL default '0', groups_price_configs int(11) unsigned NOT NULL default '0',
groups_price_options int(11) unsigned NOT NULL default '0', groups_price_options int(11) unsigned NOT NULL default '0',
groups_price_boards int(11) unsigned NOT NULL default '0', groups_price_boards int(11) unsigned NOT NULL default '0',
groups_price_adolescents_age int(11) unsigned NOT NULL default '0',
groups_price_tax_label varchar(255) DEFAULT '' NOT NULL,
main_season_from date DEFAULT NULL, main_season_from date DEFAULT NULL,
main_season_to date DEFAULT NULL, main_season_to date DEFAULT NULL,
@@ -29,6 +29,7 @@ class IconViewHelper extends AbstractTagBasedViewHelper
$content = sprintf('<use href="%s#icon-%s"></use>', $imageUrl, $this->arguments['icon']); $content = sprintf('<use href="%s#icon-%s"></use>', $imageUrl, $this->arguments['icon']);
$this->tag->setContent($content); $this->tag->setContent($content);
$this->tag->addAttributes(['aria-hidden' => 'true']);
return $this->tag->render(); return $this->tag->render();
} }
@@ -41,6 +41,8 @@ class LazyLoadImageViewHelper extends ImageViewHelper
$classItems[] = 'lazyload'; $classItems[] = 'lazyload';
$classes = implode(' ', array_unique($classItems)); $classes = implode(' ', array_unique($classItems));
$this->tag->addAttribute('class', $classes); $this->tag->addAttribute('class', $classes);
// ensure a11y compliance
$this->tag->removeAttribute('title');
return $this->tag->render(); return $this->tag->render();
} }
@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<!-- Symbols --> <!-- Symbols -->
<symbol id="icon-menu" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></symbol> <symbol id="icon-menu" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></symbol>
<symbol id="icon-chevron-down" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></symbol> <symbol id="icon-chevron-down" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></symbol>

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

@@ -16,13 +16,15 @@
Einzelne Nächte sind nur auf Anfrage buchbar Einzelne Nächte sind nur auf Anfrage buchbar
</p> </p>
</div> </div>
<div class="mb-8" <div class="mb-8 grid sm:grid-cols-2 gap-8"
v-show="!submitted"> v-show="!submitted">
<div>
<label class="mb-2 font-bold"> <label class="mb-2 font-bold">
Personenzahl Personenzahl
</label> </label>
<input type="number" <input type="number"
class="form-field" class="form-field"
min="30"
v-model.number="selectedPax" v-model.number="selectedPax"
@change="onPaxUpdated()" @change="onPaxUpdated()"
v-show="nightsCount > 0"/> v-show="nightsCount > 0"/>
@@ -31,6 +33,60 @@
Bitte zuerst einen Zeitraum wählen Bitte zuerst einen Zeitraum wählen
</p> </p>
</div> </div>
<div>
<label class="mb-2 font-bold">
davon Kinder 0-3 Jahre
</label>
<input type="number"
class="form-field"
min="0"
v-model.number="childrenCount"
@change="onPaxUpdated()"
v-show="nightsCount > 0"/>
<p class="text-sm italic"
v-show="nightsCount === 0">
Bitte zuerst einen Zeitraum wählen
</p>
<p class="text-sm italic"
v-show="nightsCount > 0">
Kinder werden nur bei Strom- und Abfallgebühren berücksichtigt.
</p>
</div>
<div>
<label class="mb-2 font-bold">
davon Kinder 4-5 Jahre*
</label>
<input type="number"
class="form-field"
min="0"
v-model.number="minorsCount"
@change="onPaxUpdated()"
v-show="nightsCount > 0"/>
<p class="text-sm italic"
v-show="nightsCount === 0">
Bitte zuerst einen Zeitraum wählen
</p>
</div>
<div v-if="adolescentsAge > 0">
<label class="mb-2 font-bold">
davon Kinder 6-{{ adolescentsAge }} Jahre*
</label>
<input type="number"
class="form-field"
min="0"
v-model.number="adolescentsCount"
@change="onPaxUpdated()"
v-show="nightsCount > 0"/>
<p class="text-sm italic"
v-show="nightsCount === 0">
Bitte zuerst einen Zeitraum wählen
</p>
</div>
<div class="sm:col-span-2 text-sm">
*Die Kosten für Essen und Kurtaxe werden wir entsprechend der Altersstruktur der Gäste in der Rechnung
anpassen.
</div>
</div>
<div class="mb-8" <div class="mb-8"
v-show="options.length > 0 && !submitted"> v-show="options.length > 0 && !submitted">
<div class="mb-2 font-bold"> <div class="mb-2 font-bold">
@@ -51,27 +107,38 @@
<div class="mb-8" <div class="mb-8"
v-show="boards.length > 0 && !submitted"> v-show="boards.length > 0 && !submitted">
<label class="mb-2 font-bold"> <label class="mb-2 font-bold">
Optionale Verpflegungsleistungen Verpflegungsleistungen
</label> </label>
<select class="form-field" <select class="form-field"
v-model="selectedBoard" v-model="selectedBoard"
v-if="selectedPax >= 30"> v-if="selectedPax >= 30">
<option :value="null">Keine Auswahl</option> <option :value="null" disabled>bitte auswählen</option>
<option v-for="board of boards" <option v-for="board of boards"
:value="board" :value="board"
:key="board.uid"> :key="board.uid">
{{ board.title }} ({{ formatCurrency(board.price) }} pro Person und Nacht) {{ board.title }} ({{ formatCurrency(board.price) }} pro Person und Nacht)
</option> </option>
</select> </select>
<div class="text-sm text-red-600 mt-1"
v-show="formErrors.board"
v-html="formErrors.board"></div>
<div v-show="selectedPax < 30"> <div v-show="selectedPax < 30">
<span class="text-red-600">Erst ab 30 Personen buchbar.</span> <span class="text-red-600">Erst ab 30 Personen buchbar.</span>
</div> </div>
</div> </div>
<div class="mb-8"> <div class="mb-8">
<div class="pb-2" v-if="submitted">
<p class="text-lg font-bold pb-2">
Vielen Dank für Deine {{ mode === 'booking' ? 'Buchung' : 'Anfrage' }}.
</p>
<p>
Für folgendes habt ihr euch entschieden:
</p>
</div>
<div class="p-2 uppercase bg-ep-primary-dark text-white w-full"> <div class="p-2 uppercase bg-ep-primary-dark text-white w-full">
Preise Preise
</div> </div>
<div class="bg-zinc-100 p-2"> <div class="bg-zinc-100 p-2 mb-2">
<table class="w-full" v-show="nightsCount > 0"> <table class="w-full" v-show="nightsCount > 0">
<tbody> <tbody>
<tr class="odd:bg-zinc-100"> <tr class="odd:bg-zinc-100">
@@ -95,7 +162,7 @@
<tr class="odd:bg-zinc-100" <tr class="odd:bg-zinc-100"
v-for="selectedOption in selectedOptions"> v-for="selectedOption in selectedOptions">
<th class="text-left py-2">{{ selectedOption.title }}</th> <th class="text-left py-2">{{ selectedOption.title }}</th>
<td>{{ formatCurrency(selectedOption.price) }}</td> <td>{{ formatCurrency(calculateOptionPrice(selectedOption)) }}</td>
</tr> </tr>
<tr class="odd:bg-zinc-100" <tr class="odd:bg-zinc-100"
v-if="priceBoard.EUR > 0 || priceBoard.CHF > 0"> v-if="priceBoard.EUR > 0 || priceBoard.CHF > 0">
@@ -103,8 +170,8 @@
<td>{{ formatCurrency(priceBoard) }}</td> <td>{{ formatCurrency(priceBoard) }}</td>
</tr> </tr>
<tr class="odd:bg-zinc-100" <tr class="odd:bg-zinc-100"
v-if="pricePax.undersubscription.EUR > 0 || pricePax.undersubscription.CHF > 0"> v-if="(priceBoard.EUR > 0 || priceBoard.CHF > 0) && (pricePax.undersubscription.EUR > 0 || pricePax.undersubscription.CHF > 0)">
<th class="text-left py-2">Kleingruppen-Verpflegungszuschlag</th> <th class="text-left py-2">Verpflegungs-Aufschlag für Gruppen unter 50 Personen</th>
<td>{{ formatCurrency(pricePax.undersubscription) }}</td> <td>{{ formatCurrency(pricePax.undersubscription) }}</td>
</tr> </tr>
<tr class="odd:bg-zinc-100" <tr class="odd:bg-zinc-100"
@@ -114,8 +181,10 @@
</tr> </tr>
<tr class="odd:bg-zinc-100"> <tr class="odd:bg-zinc-100">
<th class="text-left py-2">Gesamtpreis</th> <th class="text-left py-2">Gesamtpreis</th>
<td><strong>{{ formatCurrency(priceTotal) }}</strong> <td>
<br><small>zzgl. vor Ort zu entrichtender Ortstaxe</small> <strong>{{ formatCurrency(priceTotal) }}</strong>
<br>
<small>{{ taxLabel }}</small>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -125,6 +194,15 @@
Bitte zuerst einen Zeitraum wählen Bitte zuerst einen Zeitraum wählen
</p> </p>
</div> </div>
<div v-if="mode === 'booking' && submitted">
Bitte beachtet, dass die Buchung bei uns eingegangen, aber <strong>noch nicht bestätigt</strong> ist.
Wir behalten uns vor, die Buchungen auf Verfügbarkeit und Länge des gewünschten Aufenthalts zu prüfen.
Erst wenn wir euch die Buchung per Mail bestätigt haben, wird sie bindend.
</div>
<div v-if="mode === 'inquiry' && submitted">
Die Anfrage ist bei uns eingegangen und wird schnellstmöglich bearbeitet. Wir melden uns telefonisch
oder per Mail bei euch.
</div>
</div> </div>
<div class="mb-8" <div class="mb-8"
v-show="nightsCount > 0 && !submitted"> v-show="nightsCount > 0 && !submitted">
@@ -144,17 +222,38 @@
v-show="formErrors.name" v-show="formErrors.name"
v-html="formErrors.name"></div> v-html="formErrors.name"></div>
</div> </div>
<div :class="{ 'has-error': formErrors.address }"> <div :class="{ 'has-error': formErrors.street }">
<label class="font-bold mb-2"> <label class="font-bold mb-2">
Adresse* Strasse, Nr.*
</label> </label>
<textarea class="form-field" <input type="text"
cols="30" class="form-field"
rows="3" v-model="street">
v-model="address"/>
<div class="text-sm text-red-600 mt-1" <div class="text-sm text-red-600 mt-1"
v-show="formErrors.address" v-show="formErrors.street"
v-html="formErrors.address"></div> v-html="formErrors.street"></div>
</div>
<div :class="{ 'has-error': formErrors.postcode }">
<label class="font-bold mb-2">
Postleitzahl*
</label>
<input type="text"
class="form-field"
v-model="postcode">
<div class="text-sm text-red-600 mt-1"
v-show="formErrors.postcode"
v-html="formErrors.postcode"></div>
</div>
<div :class="{ 'has-error': formErrors.city }">
<label class="font-bold mb-2">
Ort*
</label>
<input type="text"
class="form-field"
v-model="city">
<div class="text-sm text-red-600 mt-1"
v-show="formErrors.city"
v-html="formErrors.city"></div>
</div> </div>
<div :class="{ 'has-error': formErrors.group }"> <div :class="{ 'has-error': formErrors.group }">
<label class="font-bold mb-2"> <label class="font-bold mb-2">
@@ -203,7 +302,7 @@
<input type="checkbox" v-model="confirmation" name="confirmation" id="confirmation"> <input type="checkbox" v-model="confirmation" name="confirmation" id="confirmation">
<span class="block ml-2"> <span class="block ml-2">
Durch Anklicken des Buttons 'Buchung abschicken' bestätige ich, dass ich die Durch Anklicken des Buttons 'Buchung abschicken' bestätige ich, dass ich die
<a href="https://www.ep-reisen.de/reisen-fuer-gruppen/infos-zusatzleistungen/reiseinfos/agbs/" <a :href="termsUrls[countryCode]"
class="text-ep-primary" target="_blank"> class="text-ep-primary" target="_blank">
Allgemeinen Geschäftsbedingungen (AGB) Allgemeinen Geschäftsbedingungen (AGB)
</a> gelesen habe und damit einverstanden bin, dass eine </a> gelesen habe und damit einverstanden bin, dass eine
@@ -235,9 +334,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="py-8 text-xl text-center font-bold" v-show="submitted">
Vielen Dank für Deine {{ mode === 'booking' ? 'Buchung' : 'Anfrage' }}. Wir werden diese schnellstmöglich bearbeiten.
</div>
<div> <div>
<img :src="logoAt" alt="" v-if="countryCode === 'AT'" class="block w-full h-auto max-w-24"> <img :src="logoAt" alt="" v-if="countryCode === 'AT'" class="block w-full h-auto max-w-24">
<img :src="logoCh" alt="" v-if="countryCode === 'CH'" class="block w-full h-auto max-w-24"> <img :src="logoCh" alt="" v-if="countryCode === 'CH'" class="block w-full h-auto max-w-24">
@@ -292,19 +388,19 @@ export default {
type: Number, type: Number,
default: 2.9, default: 2.9,
}, },
undersubscriptionEur: { undersubscription30Eur: {
type: Number, type: Number,
default: 5.0, default: 5.0,
}, },
undersubscriptionChf: { undersubscription30Chf: {
type: Number, type: Number,
default: 5.0, default: 5.0,
}, },
undersubscriptionExtEur: { undersubscription40Eur: {
type: Number, type: Number,
default: 2.5, default: 2.5,
}, },
undersubscriptionExtChf: { undersubscription40Chf: {
type: Number, type: Number,
default: 2.5, default: 2.5,
}, },
@@ -334,6 +430,14 @@ export default {
type: Number, type: Number,
default: 5, default: 5,
}, },
taxLabel: {
type: String,
default: 'zzgl. vor Ort zu entrichtender Ortstaxe',
},
adolescentsAge: {
type: Number,
default: 0,
}
}, },
data() { data() {
return { return {
@@ -348,16 +452,26 @@ export default {
selectedTo: null, selectedTo: null,
selectedRange: [], selectedRange: [],
selectedPax: 30, selectedPax: 30,
childrenCount: 0,
minorsCount: 0,
adolescentsCount: 0,
selectedOptions: [], selectedOptions: [],
selectedBoard: null, selectedBoard: null,
name: null, name: '',
email: null, email: '',
phone: null, phone: '',
remarks: null, remarks: '',
group: null, group: '',
address: null, street: '',
postcode: '',
city: '',
mode: 'booking', mode: 'booking',
confirmation: false, confirmation: false,
termsUrls: {
AT: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AGB_Gruppen_CLLT_Touristik_GmbH.pdf',
CH: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AVB_Gruppen_AlpineVacation_GmbH.pdf',
IT: 'https://www.ep-reisen.de/fileadmin/user_upload/allgemein/AGB_Gruppen/AGB_Gruppen_E_P_Reisen.pdf',
}
} }
}, },
methods: { methods: {
@@ -380,6 +494,28 @@ export default {
return price return price
}, },
calculateOptionPrice(option) {
let price = {
EUR: 0,
CHF: 0,
};
let optionPriceEUR = option.price.EUR
let optionPriceCHF = option.price.CHF
if (1 === option.type) {
price.EUR = optionPriceEUR;
price.CHF = optionPriceCHF;
} else if (2 === option.type) {
price.EUR = optionPriceEUR * this.paxCount;
price.CHF = optionPriceCHF * this.paxCount;
} else if (3 === option.type) {
price.EUR = optionPriceEUR * this.nightsCount;
price.CHF = optionPriceCHF * this.nightsCount;
} else if (4 === option.type) {
price.EUR = optionPriceEUR * this.nightsCount * this.paxCount;
price.CHF = optionPriceCHF * this.nightsCount * this.paxCount;
}
return price;
},
formatOptionPriceType(option) { formatOptionPriceType(option) {
if (2 === option.type) { if (2 === option.type) {
return ` (${this.formatCurrency(option.price)} pro Person)` return ` (${this.formatCurrency(option.price)} pro Person)`
@@ -431,13 +567,19 @@ export default {
'tx_epproducts_ajax[groupsPriceInquiry][confirmation]': confirmation ? '1' : '0', 'tx_epproducts_ajax[groupsPriceInquiry][confirmation]': confirmation ? '1' : '0',
'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name ? this.name : '', 'tx_epproducts_ajax[groupsPriceInquiry][name]': this.name ? this.name : '',
'tx_epproducts_ajax[groupsPriceInquiry][group]': this.group ? this.group : '', 'tx_epproducts_ajax[groupsPriceInquiry][group]': this.group ? this.group : '',
'tx_epproducts_ajax[groupsPriceInquiry][address]': this.address ? this.address : '', 'tx_epproducts_ajax[groupsPriceInquiry][street]': this.street ? this.street : '',
'tx_epproducts_ajax[groupsPriceInquiry][postcode]': this.postcode ? this.postcode : '',
'tx_epproducts_ajax[groupsPriceInquiry][city]': this.city ? this.city : '',
'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email? this.email : '', 'tx_epproducts_ajax[groupsPriceInquiry][email]': this.email? this.email : '',
'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone ? this.phone : '', 'tx_epproducts_ajax[groupsPriceInquiry][phone]': this.phone ? this.phone : '',
'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks, 'tx_epproducts_ajax[groupsPriceInquiry][remarks]': this.remarks,
'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'), 'tx_epproducts_ajax[groupsPriceInquiry][dateFrom]': this.selectedFrom.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'), 'tx_epproducts_ajax[groupsPriceInquiry][dateTo]': this.selectedTo.format('DD.MM.YYYY'),
'tx_epproducts_ajax[groupsPriceInquiry][nights]': this.nightsCount,
'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.selectedPax, 'tx_epproducts_ajax[groupsPriceInquiry][pax]': this.selectedPax,
'tx_epproducts_ajax[groupsPriceInquiry][children]': this.childrenCount,
'tx_epproducts_ajax[groupsPriceInquiry][minors]': this.minorsCount,
'tx_epproducts_ajax[groupsPriceInquiry][adolescents]': this.adolescentsCount,
'tx_epproducts_ajax[groupsPriceInquiry][summary][paxBase]': this.formatCurrency(this.pricePax.base), 'tx_epproducts_ajax[groupsPriceInquiry][summary][paxBase]': this.formatCurrency(this.pricePax.base),
'tx_epproducts_ajax[groupsPriceInquiry][summary][paxAdditional]': this.formatCurrency(this.pricePax.additional), 'tx_epproducts_ajax[groupsPriceInquiry][summary][paxAdditional]': this.formatCurrency(this.pricePax.additional),
'tx_epproducts_ajax[groupsPriceInquiry][summary][undersubscription]': this.formatCurrency(this.pricePax.undersubscription), 'tx_epproducts_ajax[groupsPriceInquiry][summary][undersubscription]': this.formatCurrency(this.pricePax.undersubscription),
@@ -474,6 +616,21 @@ export default {
}, },
}, },
computed: { computed: {
isSelfCatering() {
let selectedBoard = this.selectedBoard
if (null === selectedBoard) {
return false
}
if ('CH' === this.countryCode) {
return selectedBoard.price.CHF === 0
} else {
return selectedBoard.price.EUR === 0
}
},
paxCount() {
// subtract number of children from pax for price calculation, but minimum 30 pax
return Math.max(this.selectedPax - this.childrenCount, 30)
},
nightsCount() { nightsCount() {
return this.selectedRange.length; return this.selectedRange.length;
}, },
@@ -506,20 +663,20 @@ export default {
base.EUR += config.price.EUR base.EUR += config.price.EUR
base.CHF += config.price.CHF base.CHF += config.price.CHF
let included = config.personsIncluded let included = config.personsIncluded
let additionalPax = this.selectedPax - included if (this.paxCount > included) {
if (this.selectedPax > included) { let additionalPax = this.paxCount - included
additional.EUR += additionalPax * config.priceAdditionalPerson.EUR additional.EUR += additionalPax * config.priceAdditionalPerson.EUR
additional.CHF += additionalPax * config.priceAdditionalPerson.CHF additional.CHF += additionalPax * config.priceAdditionalPerson.CHF
} }
} }
} }
} }
if (this.selectedBoard && this.selectedPax < 40) { if (false === this.isSelfCatering && this.paxCount < 40) {
undersubscription.EUR = this.selectedPax * this.undersubscriptionEur * this.selectedRange.length undersubscription.EUR = this.paxCount * this.undersubscription30Eur * this.selectedRange.length
undersubscription.CHF = this.selectedPax * this.undersubscriptionChf * this.selectedRange.length undersubscription.CHF = this.paxCount * this.undersubscription30Chf * this.selectedRange.length
} else if (this.selectedBoard && this.selectedPax < 50) { } else if (false === this.isSelfCatering && this.paxCount < 50) {
undersubscription.EUR = this.selectedPax * this.undersubscriptionExtEur * this.selectedRange.length undersubscription.EUR = this.paxCount * this.undersubscription40Eur * this.selectedRange.length
undersubscription.CHF = this.selectedPax * this.undersubscriptionExtChf * this.selectedRange.length undersubscription.CHF = this.paxCount * this.undersubscription40Chf * this.selectedRange.length
} }
return {base, additional, undersubscription} return {base, additional, undersubscription}
@@ -530,21 +687,9 @@ export default {
CHF: 0, CHF: 0,
}; };
for (let option of this.selectedOptions) { for (let option of this.selectedOptions) {
let optionPriceEUR = option.price.EUR let optionPrice = this.calculateOptionPrice(option)
let optionPriceCHF = option.price.CHF price.EUR += optionPrice.EUR;
if (1 === option.type) { price.CHF += optionPrice.CHF;
price.EUR += optionPriceEUR;
price.CHF += optionPriceCHF;
} else if (2 === option.type) {
price.EUR += optionPriceEUR * this.selectedPax;
price.CHF += optionPriceCHF * this.selectedPax;
} else if (3 === option.type) {
price.EUR += optionPriceEUR * this.nightsCount;
price.CHF += optionPriceCHF * this.nightsCount;
} else if (4 === option.type) {
price.EUR += optionPriceEUR * this.nightsCount * this.selectedPax;
price.CHF += optionPriceCHF * this.nightsCount * this.selectedPax;
}
} }
return price; return price;
}, },
@@ -554,8 +699,8 @@ export default {
CHF: 0, CHF: 0,
} }
if (this.selectedBoard) { if (this.selectedBoard) {
price.EUR = this.selectedBoard.price.EUR * this.nightsCount * this.selectedPax; price.EUR = this.selectedBoard.price.EUR * this.nightsCount * this.paxCount;
price.CHF = this.selectedBoard.price.CHF * this.nightsCount * this.selectedPax; price.CHF = this.selectedBoard.price.CHF * this.nightsCount * this.paxCount;
} }
return price return price
}, },
@@ -121,6 +121,11 @@ renderables:
type: Textarea type: Textarea
identifier: message identifier: message
label: 'Deine Kontaktanfrage' label: 'Deine Kontaktanfrage'
-
defaultValue: ''
type: Text
identifier: bookingNumber
label: 'Buchungsnummer (falls vorhanden)'
- -
type: LinkedCheckbox type: LinkedCheckbox
identifier: privacypolicyaccepted identifier: privacypolicyaccepted
@@ -87,12 +87,21 @@
<trans-unit id="tx_eptheme.message.group.1221560718"> <trans-unit id="tx_eptheme.message.group.1221560718">
<source>Bitte angeben</source> <source>Bitte angeben</source>
</trans-unit> </trans-unit>
<trans-unit id="tx_eptheme.message.address.1221560718"> <trans-unit id="tx_eptheme.message.street.1221560718">
<source>Bitte angeben</source>
</trans-unit>
<trans-unit id="tx_eptheme.message.postcode.1221560718">
<source>Bitte angeben</source>
</trans-unit>
<trans-unit id="tx_eptheme.message.city.1221560718">
<source>Bitte angeben</source> <source>Bitte angeben</source>
</trans-unit> </trans-unit>
<trans-unit id="tx_eptheme.message.phone.1221560718"> <trans-unit id="tx_eptheme.message.phone.1221560718">
<source>Bitte angeben</source> <source>Bitte angeben</source>
</trans-unit> </trans-unit>
<trans-unit id="tx_eptheme.message.board.1221560910">
<source>Bitte auswählen</source>
</trans-unit>
<trans-unit id="tx_eptheme.message.contactForm.firstName.1221560718"> <trans-unit id="tx_eptheme.message.contactForm.firstName.1221560718">
<source>Bitte angeben</source> <source>Bitte angeben</source>
</trans-unit> </trans-unit>
@@ -264,6 +264,10 @@
font-weight:normal; font-weight:normal;
text-decoration:underline; text-decoration:underline;
} }
table.data th,
table.data td {
vertical-align: top;
}
@media only screen and (min-width:768px){ @media only screen and (min-width:768px){
.templateContainer { .templateContainer {
width:600px !important; width:600px !important;
@@ -4,14 +4,20 @@
<f:cObject typoscriptObjectPath="lib.searchBarCollapsed"/> <f:cObject typoscriptObjectPath="lib.searchBarCollapsed"/>
<f:alias map="{searchbarCollapsed: 1}"> <f:alias map="{searchbarCollapsed: 1}">
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
</f:alias> </f:alias>
<main>
<f:render section="Content"/> <f:render section="Content"/>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/> <f:render partial="Search/MobileSearch" arguments="{_all}"/>
</aside>
<f:render partial="Config" arguments="{_all}" /> <f:render partial="Config" arguments="{_all}" />
</html> </html>
@@ -2,22 +2,30 @@
xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSlider" arguments="{_all}"/> <f:render partial="HeaderSlider" arguments="{_all}"/>
</header>
<main>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else> <f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else> </f:else>
</f:if> </f:if>
<f:render section="Content"/> <f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else> <f:else>
<f:render partial="Search/MobileSearch" arguments="{_all}"/> <f:render partial="Search/MobileSearch" arguments="{_all}"/>
</f:else> </f:else>
</f:if> </f:if>
</aside>
<f:render partial="Config" arguments="{_all}" /> <f:render partial="Config" arguments="{_all}" />
</html> </html>
@@ -3,12 +3,20 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="EventHero" arguments="{_all}"/> <f:render partial="EventHero" arguments="{_all}"/>
<f:render partial="EventDisturber" arguments="{headline: settings.eventDisturberHeadline, listItems: settings.eventDisturberListitems, buttonLabel: settings.eventDisturberButtonLabel, buttonLink: settings.eventDisturberButtonLink}"/> <f:render partial="EventDisturber" arguments="{headline: settings.eventDisturberHeadline, listItems: settings.eventDisturberListitems, buttonLabel: settings.eventDisturberButtonLabel, buttonLink: settings.eventDisturberButtonLink}"/>
</header>
<main>
<f:render section="Content"/> <f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
</aside>
<f:render partial="Config" arguments="{_all}" /> <f:render partial="Config" arguments="{_all}" />
</html> </html>
@@ -3,16 +3,22 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else> <f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else> </f:else>
</f:if> </f:if>
</header>
<main>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Content"/> <f:render section="Content"/>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
@@ -3,12 +3,15 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:else> <f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else> </f:else>
</f:if> </f:if>
</header>
<main>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Header" optional="true"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8"> <div class="container grid grid-cols-3 gap-8">
@@ -20,7 +23,10 @@
</div> </div>
</div> </div>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
@@ -3,7 +3,10 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
</header>
<main>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Header" optional="true"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8"> <div class="container grid grid-cols-3 gap-8">
@@ -15,7 +18,10 @@
</div> </div>
</div> </div>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
@@ -3,12 +3,15 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:else> <f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else> </f:else>
</f:if> </f:if>
</header>
<main>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Header" optional="true"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8"> <div class="container grid grid-cols-3 gap-8">
@@ -20,7 +23,10 @@
</div> </div>
</div> </div>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
@@ -3,8 +3,11 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:render partial="Mainnav" section="Main" arguments="{_all}"/> <f:render partial="Mainnav" section="Main" arguments="{_all}"/>
<header>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
</header>
<main>
<f:render section="Header" optional="true"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8"> <div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-2"> <div class="col-span-3 lg:col-span-2">
@@ -15,7 +18,10 @@
</div> </div>
</div> </div>
<f:render section="Teasers"/> <f:render section="Teasers"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="ContactBar" arguments="{_all}"/> <f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" arguments="{_all}"/> <f:render partial="Topnav" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
@@ -3,13 +3,13 @@
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers" xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="container bg-white flex items-center justify-between"> <header class="container bg-white flex items-center justify-between">
<f:link.typolink parameter="{f:if(condition: settings.logoLink, then: settings.logoLink, else: settings.defaultHomeUid)}" <f:link.typolink parameter="{f:if(condition: settings.logoLink, then: settings.logoLink, else: settings.defaultHomeUid)}"
class="block lg:pb-4" class="block lg:pb-4"
title="zur Startseite"> title="zur Startseite">
<f:image src="Logo" <f:image src="{settings.logoImage}"
class="block w-auto {ep:themeClasses(classes: '{ep: \'h-10 lg:h-12\', sbw: \'h-14 lg:h-24\', snz: \'h-14 lg:h-24\', suz: \'h-14 lg:h-24\', uch: \'h-12 lg:h-20\', ser: \'h-10 lg:h-12\'}', key: settings.themekey)}" class="block w-auto {ep:themeClasses(classes: '{ep: \'h-10 lg:h-12\', sbw: \'h-14 lg:h-24\', snz: \'h-14 lg:h-24\', suz: \'h-14 lg:h-24\', uch: \'h-12 lg:h-20\', ser: \'h-10 lg:h-12\'}', key: settings.themekey)}"
alt="Logo"/> alt="Bildmarke"/>
</f:link.typolink> </f:link.typolink>
<f:link.typolink parameter="{settings.myEpPageUid}" <f:link.typolink parameter="{settings.myEpPageUid}"
title="My E&P" title="My E&P"
@@ -17,8 +17,12 @@
<div class="text-xl lg:text-2xl font-light leading-none">Login</div> <div class="text-xl lg:text-2xl font-light leading-none">Login</div>
<div class="text-lg lg:text-xl font-medium leading-none">My E&P</div> <div class="text-lg lg:text-xl font-medium leading-none">My E&P</div>
</f:link.typolink> </f:link.typolink>
</div> </header>
<main>
<f:render section="Content"/> <f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
</footer>
<f:render partial="Config" arguments="{_all}" /> <f:render partial="Config" arguments="{_all}" />
</html> </html>
@@ -5,7 +5,7 @@
<f:section name="Teaser"> <f:section name="Teaser">
<f:if condition="{concept}"> <f:if condition="{concept}">
<a href="{f:uri.typolink(parameter: concept.detailPage)}" class="flex flex-col"> <div class="relative flex flex-col">
<div class="relative"> <div class="relative">
<f:alias map="{teaserImage: concept.teaserImage, defaultImage: concept.headerImage}"> <f:alias map="{teaserImage: concept.teaserImage, defaultImage: concept.headerImage}">
<f:if condition="{teaserImage}"> <f:if condition="{teaserImage}">
@@ -33,17 +33,19 @@
</f:else> </f:else>
</f:if> </f:if>
</f:alias> </f:alias>
<div class="absolute top-0 left-0 inset-0 bg-concept-{concept.code}/70"> <div class="absolute z-10 inset-0 bg-concept-{concept.code}/70">
<div class="flex h-full w-full p-4 items-end headline--3 text-white"> <f:link.typolink parameter="{concept.detailPage}"
class="flex h-full w-full p-4 items-end headline--3 text-white">
{concept.name} {concept.name}
</div> <span class="absolute inset-0"></span>
</f:link.typolink>
</div> </div>
</div> </div>
<div class="p-4 flex-1"> <div class="p-4 flex-1">
{concept.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()} {concept.teaser -> f:format.crop(maxCharacters: settings.teaserTextMaxChars) -> f:format.html()}
</div> </div>
<div class="bg-concept-{concept.code} h-1 w-20"></div> <div class="bg-concept-{concept.code} h-1 w-20"></div>
</a> </div>
</f:if> </f:if>
</f:section> </f:section>
@@ -8,7 +8,7 @@
<div>{data.bodytext -> f:format.html()}</div> <div>{data.bodytext -> f:format.html()}</div>
<div> <div>
{data.flexForm.buttons -> v:iterator.first() -> v:variable.set(name: 'button')} {data.flexForm.buttons -> v:iterator.first() -> v:variable.set(name: 'button')}
<a href="{f:uri.typolink(parameter: button.container.link)}" class="btn btn-info" title="{button.container.label -> f:format.raw()}">{button.container.label -> f:format.raw()}</a> <a href="{f:uri.typolink(parameter: button.container.link)}" class="btn btn-info">{button.container.label -> f:format.raw()}</a>
</div> </div>
</div> </div>
@@ -16,7 +16,7 @@
<picture class="w-full h-full"> <picture class="w-full h-full">
<source srcset="{f:uri.image(image: image, width: '1370')}" media="(min-width: 768px)"> <source srcset="{f:uri.image(image: image, width: '1370')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: image, height: '640', cropVariant: 'mobile')}" media="(max-width: 767px)"> <source srcset="{f:uri.image(image: image, height: '640', cropVariant: 'mobile')}" media="(max-width: 767px)">
<f:image image="{image}" cropVariant="mobile" height="640" class="w-full h-auto" alt="{image.alternative}"/> <img src="{f:uri.image(image: image, cropVariant: 'mobile', height: '640')}" class="w-full h-auto" alt="{image.alternative}">
</picture> </picture>
</div> </div>
</f:if> </f:if>
@@ -34,6 +34,7 @@
</f:if> </f:if>
<f:if condition="{slide.button}"> <f:if condition="{slide.button}">
<f:link.typolink parameter="{slide.page_uid}" <f:link.typolink parameter="{slide.page_uid}"
additionalAttributes="{'tabindex': '-1'}"
class="button bg-button inline-block"> class="button bg-button inline-block">
{slide.button} {slide.button}
</f:link.typolink> </f:link.typolink>
@@ -44,7 +45,8 @@
<f:if condition="{slide.skipass} || {badge}"> <f:if condition="{slide.skipass} || {badge}">
<div class="herounit__badge-wrapper"> <div class="herounit__badge-wrapper">
<f:if condition="{slide.skipass}"> <f:if condition="{slide.skipass}">
<f:link.typolink parameter="{settings.skipassPageUid}"> <f:link.typolink parameter="{settings.skipassPageUid}"
additionalAttributes="{'tabindex': '-1'}">
<div class="herounit__badge herounit__badge--skipass"> <div class="herounit__badge herounit__badge--skipass">
<div class="uppercase text-4xl font-bold leading-7 mb-2">Ski-<br>Pass</div> <div class="uppercase text-4xl font-bold leading-7 mb-2">Ski-<br>Pass</div>
<div class="uppercase">inklusive</div> <div class="uppercase">inklusive</div>
@@ -53,14 +55,15 @@
</f:if> </f:if>
<f:if condition="{badge}"> <f:if condition="{badge}">
<f:alias map="{badgeData: badge.0.data}"> <f:alias map="{badgeData: badge.0.data}">
<f:link.typolink parameter="{badgeData.link}"> <f:link.typolink parameter="{badgeData.link}"
additionalAttributes="{'tabindex': '-1'}">
<div class="herounit__badge herounit__badge--custom"> <div class="herounit__badge herounit__badge--custom">
<svg viewBox="0 0 200 200" <svg viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMinYMin meet" preserveAspectRatio="xMinYMin meet"
class="w-full h-full"> class="w-full h-full">
<circle fill="currentColor" class="text-ep-primary-dark/50" cx="100" cy="100" r="100"/> <circle fill="currentColor" class="text-ep-primary/50" cx="100" cy="100" r="100"/>
<circle fill="currentColor" class="text-ep-primary-dark" cx="100" cy="100" r="70"/> <circle fill="currentColor" class="text-ep-primary" cx="100" cy="100" r="70"/>
</svg> </svg>
<div class="absolute top-0 left-0 inset-0 flex flex-col items-center justify-center p-8"> <div class="absolute top-0 left-0 inset-0 flex flex-col items-center justify-center p-8">
<span class="block text-center text-white uppercase font-bold leading-none text-xl">{badgeData.title -> f:format.raw()}</span> <span class="block text-center text-white uppercase font-bold leading-none text-xl">{badgeData.title -> f:format.raw()}</span>
@@ -7,7 +7,7 @@
<f:alias map="{image: slide.image.0}"> <f:alias map="{image: slide.image.0}">
<f:if condition="{image}"> <f:if condition="{image}">
<picture> <picture>
<source srcset="{f:uri.image(image: image, width: '1370')}" media="(min-width: 768px)"> <source srcset="{f:uri.image(image: image, width: '1440')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: image, height: '320', cropVariant: 'mobile')}" media="(max-width: 767px)"> <source srcset="{f:uri.image(image: image, height: '320', cropVariant: 'mobile')}" media="(max-width: 767px)">
<f:image class="block w-full h-full object-cover" <f:image class="block w-full h-full object-cover"
image="{image}" image="{image}"
@@ -24,7 +24,7 @@
data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid, absolute: 1)}" data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid, absolute: 1)}"
data-lightbox-type-value="external" data-lightbox-type-value="external"
data-action="lightbox#open"> data-action="lightbox#open">
Preisberechnung &amp; direkt buchen Preisrechner/Buchungstool
</button> </button>
</div> </div>
</f:if> </f:if>
@@ -4,6 +4,15 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"> xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<div class="odd:bg-zinc-50 p-4" data-rte-content> <div class="odd:bg-zinc-50 p-4" data-rte-content>
<f:if condition="{journey.byBus}">
<h3>
Busanreise
</h3>
<div class="pb-8">
{journey.byBus -> f:format.html()}
</div>
</f:if>
<f:if condition="{journey.byCar}">
<h3> <h3>
Eigenanreise Eigenanreise
</h3> </h3>
@@ -16,11 +25,6 @@
height="430c+100" height="430c+100"
alt="{journey.image.alternative}" /> alt="{journey.image.alternative}" />
</f:if> </f:if>
<f:if condition="{journey.byBus}">
<h3>
Busanreise
</h3>
{journey.byBus -> f:format.html()}
</f:if> </f:if>
</div> </div>
@@ -11,7 +11,7 @@
title="zur Startseite"> title="zur Startseite">
<f:image src="{settings.logoImage}" <f:image src="{settings.logoImage}"
class="block w-auto {ep:themeClasses(classes: '{ep: \'h-10 lg:h-12\', sbw: \'h-14 lg:h-24\', snz: \'h-14 lg:h-24\', suz: \'h-14 lg:h-24\', uch: \'h-12 lg:h-20\', ser: \'h-10 lg:h-12\'}', key: settings.themekey)}" class="block w-auto {ep:themeClasses(classes: '{ep: \'h-10 lg:h-12\', sbw: \'h-14 lg:h-24\', snz: \'h-14 lg:h-24\', suz: \'h-14 lg:h-24\', uch: \'h-12 lg:h-20\', ser: \'h-10 lg:h-12\'}', key: settings.themekey)}"
alt="Logo"/> alt="Bildmarke"/>
</f:link.typolink> </f:link.typolink>
<div class="hidden hover-hover:lg:block"> <div class="hidden hover-hover:lg:block">
<f:render section="FirstLevel" arguments="{_all}"/> <f:render section="FirstLevel" arguments="{_all}"/>
@@ -43,10 +43,10 @@
</f:section> </f:section>
<f:section name="FirstLevel"> <f:section name="FirstLevel">
<nav role="menu"> <nav role="navigation" aria-label="Hauptnavigation">
<ul class="m-0 p-0 list-none flex items-center justify-end space-x-4"> <ul class="m-0 p-0 list-none flex items-center justify-end space-x-4" role="menubar">
<f:for each="{mainnav}" as="topitem"> <f:for each="{mainnav}" as="topitem">
<li class="group pb-4"> <li class="group pb-4" role="none">
<f:if condition="{topitem.children}"> <f:if condition="{topitem.children}">
<f:then> <f:then>
<a href="{f:uri.typolink(parameter: topitem.data.uid)}" <a href="{f:uri.typolink(parameter: topitem.data.uid)}"
@@ -69,7 +69,7 @@
</f:for> </f:for>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else> <f:else>
<li class="pb-4"> <li class="pb-4" role="none">
<f:if condition="{searchbarCollapsed}"> <f:if condition="{searchbarCollapsed}">
<f:then> <f:then>
<button type="button" <button type="button"
@@ -80,7 +80,7 @@
</button> </button>
</f:then> </f:then>
<f:else> <f:else>
<f:link.typolink parameter="{settings.defaultSearchPageUid}" title="zur Suche"> <f:link.typolink parameter="{settings.defaultSearchPageUid}" title="zur Suche" additionalAttributes="{'role': 'menuitem'}">
{ep:icon(icon: 'search', class: 'w-6 h-6')} {ep:icon(icon: 'search', class: 'w-6 h-6')}
</f:link.typolink> </f:link.typolink>
</f:else> </f:else>
@@ -96,11 +96,11 @@
<div class="absolute top-100 left-0 inset-x-0 pt-4 invisible opacity-0 group-hover:visible group-hover:opacity-100 transition-opacity duration-300 ease-in-out"> <div class="absolute top-100 left-0 inset-x-0 pt-4 invisible opacity-0 group-hover:visible group-hover:opacity-100 transition-opacity duration-300 ease-in-out">
<div class="bg-white grid grid-cols-3 lg:grid-cols-4 gap-8 p-4 shadow-lg rounded"> <div class="bg-white grid grid-cols-3 lg:grid-cols-4 gap-8 p-4 shadow-lg rounded">
<f:for each="{topitem.children}" as="subitem"> <f:for each="{topitem.children}" as="subitem">
<ul class="m-0 p-0 list-none"> <ul class="m-0 p-0 list-none" role="menu">
<li> <li role="none">
<f:if condition="{subitem.doktype} == 3"> <f:if condition="{subitem.doktype} == 3">
<f:then> <f:then>
<a href="{subitem.link}" target="_blank"> <a href="{subitem.link}" target="_blank" role="menuitem">
<f:if condition="{subitem.data.tx_eptheme_context_country_code}"> <f:if condition="{subitem.data.tx_eptheme_context_country_code}">
{ep:icon(icon: 'flag-{subitem.data.tx_eptheme_context_country_code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6')} {ep:icon(icon: 'flag-{subitem.data.tx_eptheme_context_country_code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6')}
</f:if> </f:if>
@@ -111,6 +111,7 @@
</f:then> </f:then>
<f:else> <f:else>
<a href="{f:uri.typolink(parameter: subitem.data.uid)}" <a href="{f:uri.typolink(parameter: subitem.data.uid)}"
role="menuitem"
class="flex items-center space-x-2 underlined"> class="flex items-center space-x-2 underlined">
<f:if condition="{subitem.data.tx_eptheme_context_country_code}"> <f:if condition="{subitem.data.tx_eptheme_context_country_code}">
{ep:icon(icon: 'flag-{subitem.data.tx_eptheme_context_country_code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6')} {ep:icon(icon: 'flag-{subitem.data.tx_eptheme_context_country_code -> f:format.case(mode: \'lower\')}', class: 'w-8 h-6')}
@@ -133,18 +134,20 @@
<f:for each="{subitem.children}" as="subsubitem"> <f:for each="{subitem.children}" as="subsubitem">
<f:if condition="{subsubitem.doktype} == 3"> <f:if condition="{subsubitem.doktype} == 3">
<f:then> <f:then>
<li> <li role="none">
<a href="{subsubitem.link}" <a href="{subsubitem.link}"
class="text-zinc-800 text-lg" class="text-zinc-800 text-lg"
target="_blank"> target="_blank"
role="menuitem">
{subsubitem.title} {subsubitem.title}
</a> </a>
</li> </li>
</f:then> </f:then>
<f:else> <f:else>
<li> <li role="none">
<a href="{f:uri.typolink(parameter: subsubitem.data.uid)}" <a href="{f:uri.typolink(parameter: subsubitem.data.uid)}"
class="text-zinc-800 text-lg"> class="text-zinc-800 text-lg"
role="menuitem">
{subsubitem.title} {subsubitem.title}
</a> </a>
</li> </li>
@@ -14,7 +14,7 @@
<f:link.typolink parameter="{f:if(condition: settings.logoLink, then: settings.logoLink, else: settings.defaultHomeUid)}" <f:link.typolink parameter="{f:if(condition: settings.logoLink, then: settings.logoLink, else: settings.defaultHomeUid)}"
class="block hover-hover:lg:pb-4" class="block hover-hover:lg:pb-4"
title="zur Startseite"> title="zur Startseite">
<f:image class="block h-10 lg:h-12 w-auto" src="{settings.logoImage}" alt="Logo"/> <f:image class="block h-10 lg:h-12 w-auto" src="{settings.logoImage}" alt="Bildmarke"/>
</f:link.typolink> </f:link.typolink>
<div class="flex hover-hover:lg:hidden items-center space-x-4"> <div class="flex hover-hover:lg:hidden items-center space-x-4">
<f:if condition="{data.tx_eptheme_hide_searchbar}"> <f:if condition="{data.tx_eptheme_hide_searchbar}">
@@ -12,8 +12,7 @@
<f:alias map="{mediaElement: '{newsItem.mediaPreviews.0}'}"> <f:alias map="{mediaElement: '{newsItem.mediaPreviews.0}'}">
<img class="block w-full h-auto lazyload" <img class="block w-full h-auto lazyload"
data-src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}" data-src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}"
alt="{mediaElement.originalResource.alternative}" alt="{mediaElement.originalResource.alternative}">
title="{mediaElement.originalResource.title}">
</f:alias> </f:alias>
</f:if> </f:if>
</f:then> </f:then>
@@ -22,8 +21,7 @@
<f:alias map="{mediaElement: '{newsItem.media.0}'}"> <f:alias map="{mediaElement: '{newsItem.media.0}'}">
<img class="block w-full h-auto lazyload" <img class="block w-full h-auto lazyload"
data-src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}" data-src="{f:uri.image(image: mediaElement, width: '640', height: '360c+50')}"
alt="{mediaElement.originalResource.alternative}" alt="{mediaElement.originalResource.alternative}">
title="{mediaElement.originalResource.title}">
</f:alias> </f:alias>
</f:if> </f:if>
</f:else> </f:else>
@@ -40,7 +38,7 @@
<time datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}"><f:format.date format="{f:translate(key:'dateFormat')}">{newsItem.datetime}</f:format.date></time> <time datetime="{f:format.date(date:newsItem.datetime, format:'Y-m-d')}"><f:format.date format="{f:translate(key:'dateFormat')}">{newsItem.datetime}</f:format.date></time>
<f:if condition="{newsItem.tags}"> in <f:if condition="{newsItem.tags}"> in
<f:for each="{newsItem.tags}" as="tag" iteration="iteration"> <f:for each="{newsItem.tags}" as="tag" iteration="iteration">
<f:link.page title="{tag.title}" pageUid="{settings.tagListPageUid}" additionalParams="{tx_news_pi1:{overwriteDemand:{tags: tag}}}"> <f:link.page pageUid="{settings.tagListPageUid}" additionalParams="{tx_news_pi1:{overwriteDemand:{tags: tag}}}">
{tag.title} {tag.title}
</f:link.page> </f:link.page>
{f:if(condition: iteration.isLast, else: ', ')} {f:if(condition: iteration.isLast, else: ', ')}
@@ -59,7 +57,7 @@
</f:if> </f:if>
</div> </div>
</n:removeMediaTags> </n:removeMediaTags>
<n:link newsItem="{newsItem}" settings="{settings}" class="button bg-button button--small" title="{newsItem.title}"> <n:link newsItem="{newsItem}" settings="{settings}" class="button bg-button button--small">
<f:translate key="more-link"/> <f:translate key="more-link"/>
</n:link> </n:link>
</div> </div>
@@ -3,7 +3,7 @@
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers" xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers"> xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:if condition="{pricetable -> f:count()} == 0"> <f:if condition="{pricetable.available -> f:count()} == 0">
<f:then> <f:then>
<div class="alert alert-info" data-rte-content> <div class="alert alert-info" data-rte-content>
{nonBookableInfo.text -> f:format.html()} {nonBookableInfo.text -> f:format.html()}
@@ -32,7 +32,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<f:for each="{pricetable}" as="row" iteration="iteration"> <f:for each="{pricetable.available}" as="row" iteration="iteration">
<tr class="even:bg-zinc-50"> <tr class="even:bg-zinc-50">
<td class="px-2 py-1 border border-zinc-200 whitespace-nowrap"> <td class="px-2 py-1 border border-zinc-200 whitespace-nowrap">
{row.roomName} {row.roomName}
@@ -88,8 +88,7 @@
data-pricetable-type-param="options" data-pricetable-type-param="options"
data-pricetable-uid-param="{row.roomUid}" data-pricetable-uid-param="{row.roomUid}"
data-cooltipz-dir="top" data-cooltipz-dir="top"
aria-label="Alle Zusatzleistungen anzeigen" aria-label="Alle Zusatzleistungen anzeigen">
role="tooltip">
{ep:icon(icon: 'plus-circle', class: 'w-4 h-4 md:w-5 md:h-5')} {ep:icon(icon: 'plus-circle', class: 'w-4 h-4 md:w-5 md:h-5')}
</button> </button>
</f:if> </f:if>
@@ -137,7 +136,7 @@
</button> </button>
</div> </div>
<div class="h-128 p-4 overflow-y-scroll bg-white rounded-b"> <div class="h-128 p-4 overflow-y-scroll bg-white rounded-b">
<f:for each="{pricetable}" as="row"> <f:for each="{pricetable.available}" as="row">
<div class="hidden" <div class="hidden"
data-modal-target="content" data-modal-target="content"
data-uid="{row.roomUid}"> data-uid="{row.roomUid}">
@@ -12,7 +12,7 @@
data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid, absolute: 1)}" data-lightbox-url-value="{f:uri.action(action: 'index', controller: 'GroupsPrice', arguments: '{hotel: hotel}', pageUid: settings.groupsPricePopupPageUid, absolute: 1)}"
data-lightbox-type-value="external" data-lightbox-type-value="external"
data-action="lightbox#open"> data-action="lightbox#open">
Preisberechnung &amp; direkt buchen Preisrechner/Buchungstool
</button> </button>
</div> </div>
</f:if> </f:if>
@@ -14,7 +14,7 @@
</f:if> </f:if>
<div class="herounit h-80 lg:h-96 mb-8"> <div class="herounit h-80 lg:h-96 mb-8">
<picture> <picture>
<source srcset="{f:uri.image(image: image, width: '1370c', height: '380c')}" media="(min-width: 768px)"> <source srcset="{f:uri.image(image: image, width: '1440c', height: '380c')}" media="(min-width: 768px)">
<source srcset="{f:uri.image(image: image, height: '320c')}" media="(max-width: 767px)"> <source srcset="{f:uri.image(image: image, height: '320c')}" media="(max-width: 767px)">
<f:image class="block w-full h-full object-cover" <f:image class="block w-full h-full object-cover"
image="{image}" image="{image}"
@@ -68,7 +68,6 @@
<f:if condition="{settings.showSkipassBadge} && {product.skipassIncluded}"> <f:if condition="{settings.showSkipassBadge} && {product.skipassIncluded}">
<div class="herounit__badge-wrapper"> <div class="herounit__badge-wrapper">
<f:link.typolink parameter="{settings.skipassPageUid}" <f:link.typolink parameter="{settings.skipassPageUid}"
title="Skipass inklusive"
class="herounit__badge herounit__badge--skipass"> class="herounit__badge herounit__badge--skipass">
<p class="uppercase text-4xl font-bold leading-7 mb-2"> <p class="uppercase text-4xl font-bold leading-7 mb-2">
Ski-<br>Pass Ski-<br>Pass
@@ -57,8 +57,7 @@
data-watchlist-toggle-target="ariaLabel" data-watchlist-toggle-target="ariaLabel"
data-action="watchlist-toggle#toggle" data-action="watchlist-toggle#toggle"
data-cooltipz-dir="top-right" data-cooltipz-dir="top-right"
aria-label="auf die Merkliste setzen" aria-label="auf die Merkliste setzen">
role="tooltip">
<svg class="product-teaser__watchlist-icon" data-watchlist-toggle-target="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path></svg> <svg class="product-teaser__watchlist-icon" data-watchlist-toggle-target="icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path></svg>
</button> </button>
</div> </div>
@@ -17,8 +17,7 @@
loaderimg="EXT:ep_theme/Resources/Public/images/lazy.png" loaderimg="EXT:ep_theme/Resources/Public/images/lazy.png"
class="block w-full h-auto" class="block w-full h-auto"
width="330" width="330"
alt="Webcam {region.name}" alt="Webcam {region.name}" />
title="Webcam {region.name}" />
</a> </a>
</div> </div>
</f:if> </f:if>
@@ -11,7 +11,7 @@
<a href="{f:uri.typolink(parameter: settings.defaultHomeUid)}" <a href="{f:uri.typolink(parameter: settings.defaultHomeUid)}"
class="block hover-hover:lg:pb-4" class="block hover-hover:lg:pb-4"
title="zur Startseite"> title="zur Startseite">
<f:image class="block h-10 lg:h-12 w-auto" src="{settings.logoImage}" alt="Logo"/> <f:image class="block h-10 lg:h-12 w-auto" src="{settings.logoImage}" alt="Bildmarke"/>
</a> </a>
<button type="button" <button type="button"
aria-label="Suche schließen" aria-label="Suche schließen"
@@ -11,7 +11,6 @@
<div class="tiles__tile lg:col-span-2 lg:row-span-1" <div class="tiles__tile lg:col-span-2 lg:row-span-1"
style="background-image:url('{f:uri.image(image: images.0)}');"> style="background-image:url('{f:uri.image(image: images.0)}');">
<f:link.typolink parameter="{images.0.link}" <f:link.typolink parameter="{images.0.link}"
title="{images.0.title -> f:format.raw()}"
class="tiles__link"> class="tiles__link">
<span class="tiles__label"> <span class="tiles__label">
{images.0.title -> f:format.raw()} {images.0.title -> f:format.raw()}
@@ -23,7 +22,6 @@
<div class="tiles__tile lg:col-span-2 lg:row-span-1" <div class="tiles__tile lg:col-span-2 lg:row-span-1"
style="background-image:url('{f:uri.image(image: images.1)}');"> style="background-image:url('{f:uri.image(image: images.1)}');">
<f:link.typolink parameter="{images.1.link}" <f:link.typolink parameter="{images.1.link}"
title="{images.1.title -> f:format.raw()}"
class="tiles__link"> class="tiles__link">
<span class="tiles__label"> <span class="tiles__label">
{images.1.title -> f:format.raw()} {images.1.title -> f:format.raw()}
@@ -35,7 +33,6 @@
<div class="tiles__tile lg:col-span-12 lg:row-span-1" <div class="tiles__tile lg:col-span-12 lg:row-span-1"
style="background-image:url('{f:uri.image(image: images.4)}');"> style="background-image:url('{f:uri.image(image: images.4)}');">
<f:link.typolink parameter="{images.4.link}" <f:link.typolink parameter="{images.4.link}"
title="{images.4.title -> f:format.raw()}"
class="tiles__link"> class="tiles__link">
<span class="tiles__label"> <span class="tiles__label">
{images.4.title -> f:format.raw()} {images.4.title -> f:format.raw()}
@@ -47,7 +44,6 @@
<div class="tiles__tile lg:col-span-4 lg:row-span-2" <div class="tiles__tile lg:col-span-4 lg:row-span-2"
style="background-image:url('{f:uri.image(image: images.2)}');"> style="background-image:url('{f:uri.image(image: images.2)}');">
<f:link.typolink parameter="{images.2.link}" <f:link.typolink parameter="{images.2.link}"
title="{images.2.title -> f:format.raw()}"
class="tiles__link"> class="tiles__link">
<span class="tiles__label"> <span class="tiles__label">
{images.2.title -> f:format.raw()} {images.2.title -> f:format.raw()}
@@ -59,7 +55,6 @@
<div class="tiles__tile lg:col-span-6 lg:row-span-2" <div class="tiles__tile lg:col-span-6 lg:row-span-2"
style="background-image:url('{f:uri.image(image: images.3)}');"> style="background-image:url('{f:uri.image(image: images.3)}');">
<f:link.typolink parameter="{images.3.link}" <f:link.typolink parameter="{images.3.link}"
title="{images.3.title -> f:format.raw()}"
class="tiles__link"> class="tiles__link">
<span class="tiles__label"> <span class="tiles__label">
{images.3.title -> f:format.raw()} {images.3.title -> f:format.raw()}
@@ -7,7 +7,7 @@
<f:else> <f:else>
<div class="topnav"> <div class="topnav">
<div class="container"> <div class="container">
<nav role="navigation" class="topnav__nav"> <nav role="navigation" aria-label="Bereichs-Navigation" class="topnav__nav">
<div class="flex items-center border-l border-white/50"> <div class="flex items-center border-l border-white/50">
<f:if condition="{settings.mainSitePid}"> <f:if condition="{settings.mainSitePid}">
<f:link.typolink parameter="{settings.mainSitePid}" <f:link.typolink parameter="{settings.mainSitePid}"
@@ -15,7 +15,7 @@
title="zur Startseite"> title="zur Startseite">
<f:image src="EXT:ep_theme/Resources/Public/images/logo.svg" <f:image src="EXT:ep_theme/Resources/Public/images/logo.svg"
class="h-5 w-auto" class="h-5 w-auto"
alt="Logo"/> alt=""/>
</f:link.typolink> </f:link.typolink>
</f:if> </f:if>
<f:link.typolink parameter="{settings.defaultHomeUid}" class="topnav__item" title="E&amp;P"> <f:link.typolink parameter="{settings.defaultHomeUid}" class="topnav__item" title="E&amp;P">
@@ -7,7 +7,7 @@
<f:section name="Main"> <f:section name="Main">
<div class="container relative"> <div class="container relative">
<div class="h-80 lg:h-128"> <div class="h-80 lg:h-128">
<f:image image="{travelinfo.headerImage}" <f:image image="{travelInfo.headerImage}"
width="1280" width="1280"
alt="Reiseinformationen" alt="Reiseinformationen"
class="block w-full h-full object-cover object-center" /> class="block w-full h-full object-cover object-center" />
@@ -18,8 +18,8 @@
</div> </div>
<div class="grid grid-cols-2 lg:grid-cols-4"> <div class="grid grid-cols-2 lg:grid-cols-4">
<f:variable name="bgColors" value="{0: 'bg-[#165883]/90', 1: 'bg-[#9DBACE]/90', 2: 'bg-[#3396E4]/90', 3: 'bg-[#0268AA]/90'}"/> <f:variable name="bgColors" value="{0: 'bg-[#165883]/90', 1: 'bg-[#9DBACE]/90', 2: 'bg-[#3396E4]/90', 3: 'bg-[#0268AA]/90'}"/>
<f:variable name="linksCount" value="{travelinfo.links -> f:count()}"/> <f:variable name="linksCount" value="{travelInfo.links -> f:count()}"/>
<f:for each="{travelinfo.links}" as="link" iteration="iteration"> <f:for each="{travelInfo.links}" as="link" iteration="iteration">
<f:render section="Link" arguments="{item: link, color: '{bgColors.{iteration.index}}'}"/> <f:render section="Link" arguments="{item: link, color: '{bgColors.{iteration.index}}'}"/>
</f:for> </f:for>
<a href="#goodtoknow" <a href="#goodtoknow"
@@ -40,13 +40,13 @@
<div class="pb-32 pt-32 lg:pt-52 -mt-24 bg-ep-primary-bg"> <div class="pb-32 pt-32 lg:pt-52 -mt-24 bg-ep-primary-bg">
<div class="container px-8 lg:px-24" data-rte-content> <div class="container px-8 lg:px-24" data-rte-content>
<h1 class="headline--underlined headline--has-subline"> <h1 class="headline--underlined headline--has-subline">
<span class="subline">{travelinfo.subline}</span> <span class="subline">{travelInfo.subline}</span>
{travelinfo.headline} {travelInfo.headline}
</h1> </h1>
{travelinfo.introText -> f:format.html()} {travelInfo.introText -> f:format.html()}
<f:if condition="{date}"> <f:if condition="{date}">
<p> <p>
{date.product.name} {date.dateStart -> f:format.date(format: 'd.m.Y')} - {date.dateEnd -> f:format.date(format: 'd.m.Y')} {travelData.label} {travelData.dateFrom -> f:format.date(format: 'd.m.Y')} - {travelData.dateTo -> f:format.date(format: 'd.m.Y')}
</p> </p>
</f:if> </f:if>
</div> </div>
@@ -63,20 +63,22 @@
</div> </div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content> <div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
<f:if condition="{date}"> <f:if condition="{date}">
<f:if condition="{travelinfo.hideAddress}"> <f:if condition="{travelInfo.hideAddress}">
<f:else> <f:else>
<h3 class="text-white"> <h3 class="text-white">
Adresse Adresse
</h3> </h3>
<p> <p>
{date.hotel.name} {travelData.hotel.name}
<br> <br>
{date.hotel.address -> f:format.nl2br()} {travelData.hotel.street}
<br>
{travelData.hotel.country} {travelData.hotel.city}
</p> </p>
</f:else> </f:else>
</f:if> </f:if>
<f:if condition="{date.pickups -> f:count()} > 0"> <f:if condition="{travelData.pickupsTo -> f:count()} > 0">
<f:if condition="{date.pickups -> f:count()} <= 3"> <f:if condition="{travelData.pickupsTo -> f:count()} <= 3">
<f:then> <f:then>
<f:render section="Pickups" arguments="{_all}"/> <f:render section="Pickups" arguments="{_all}"/>
</f:then> </f:then>
@@ -98,11 +100,11 @@
</f:if> </f:if>
</f:if> </f:if>
</f:if> </f:if>
<f:if condition="{travelinfo.selfArrangedText}"> <f:if condition="{travelInfo.selfArrangedText}">
<h3 class="text-white"> <h3 class="text-white">
Eigenanreise Eigenanreise
</h3> </h3>
{travelinfo.selfArrangedText -> f:format.html()} {travelInfo.selfArrangedText -> f:format.html()}
</f:if> </f:if>
</div> </div>
</div> </div>
@@ -114,13 +116,13 @@
</h2> </h2>
</div> </div>
<div class="container px-4 lg:px-24 flex flex-col space-y-16 pb-16"> <div class="container px-4 lg:px-24 flex flex-col space-y-16 pb-16">
<f:for each="{travelinfo.teasers}" as="teaser"> <f:for each="{travelInfo.teasers}" as="teaser">
<f:render section="Teaser" arguments="{_all}"/> <f:render section="Teaser" arguments="{_all}"/>
</f:for> </f:for>
</div> </div>
<div id="goodtoknow" class="bg-ep-primary-bg"> <div id="goodtoknow" class="bg-ep-primary-bg">
<div class="container"> <div class="container">
<f:if condition="{travelinfo.additionalInfo}"> <f:if condition="{travelInfo.additionalInfo}">
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#B48F1D] px-8 lg:px-0 py-8"> <div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#B48F1D] px-8 lg:px-0 py-8">
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8"> <div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
<div class="w-32 h-32 xl:w-16 xl:h-16 text-[#F9C700]"> <div class="w-32 h-32 xl:w-16 xl:h-16 text-[#F9C700]">
@@ -131,11 +133,11 @@
</div> </div>
</div> </div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content> <div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
{travelinfo.additionalInfo -> f:format.html()} {travelInfo.additionalInfo -> f:format.html()}
</div> </div>
</div> </div>
</f:if> </f:if>
<f:if condition="{travelinfo.importantPhoneNumbers}"> <f:if condition="{travelInfo.importantPhoneNumbers}">
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#F8C62F] px-8 lg:px-0 py-8"> <div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#F8C62F] px-8 lg:px-0 py-8">
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8"> <div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
<div class="text-2xl lg:text-4xl text-white uppercase font-bold"> <div class="text-2xl lg:text-4xl text-white uppercase font-bold">
@@ -143,18 +145,18 @@
</div> </div>
</div> </div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content> <div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
<f:if condition="{date.guide}"> <f:if condition="{travelData.guide}">
<p> <p>
<strong>Busbegleitung / Reiseleitung</strong> <strong>Busbegleitung / Reiseleitung</strong>
<br> <br>
{date.guide.name} {date.guide.phone} {travelData.guide.name} {travelData.guide.phone}
</p> </p>
</f:if> </f:if>
{travelinfo.importantPhoneNumbers -> f:format.html()} {travelInfo.importantPhoneNumbers -> f:format.html()}
</div> </div>
</div> </div>
</f:if> </f:if>
<f:if condition="{travelinfo.importantLinks}"> <f:if condition="{travelInfo.importantLinks}">
<div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#18527B] px-8 lg:px-0 py-8"> <div class="flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-white bg-[#18527B] px-8 lg:px-0 py-8">
<div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8"> <div class="lg:w-1/4 flex flex-col space-y-4 pb-8 lg:pb-0 lg:px-8">
<div class="text-2xl lg:text-4xl text-white uppercase font-bold"> <div class="text-2xl lg:text-4xl text-white uppercase font-bold">
@@ -162,18 +164,18 @@
</div> </div>
</div> </div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content> <div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
{travelinfo.importantLinks -> f:format.html()} {travelInfo.importantLinks -> f:format.html()}
</div> </div>
</div> </div>
</f:if> </f:if>
</div> </div>
</div> </div>
<div class="container px-4 lg:px-24 py-16"> <div class="container px-4 lg:px-24 py-16">
{travelinfo.footerText -> f:format.html()} {travelInfo.footerText -> f:format.html()}
<f:image src="EXT:ep_theme/Resources/Public/images/signature_ep_team.png" class="block w-full max-w-64 h-auto mt-8" alt="" /> <f:image src="EXT:ep_theme/Resources/Public/images/signature_ep_team.png" class="block w-full max-w-64 h-auto mt-8" alt="" />
</div> </div>
<div class="container"> <div class="container">
<f:image image="{travelinfo.footerImage}" width="1028" class="block w-full h-auto" alt="" /> <f:image image="{travelInfo.footerImage}" width="1028" class="block w-full h-auto" alt="" />
</div> </div>
</f:section> </f:section>
@@ -196,20 +198,28 @@
<h2 class="headline--primary"> <h2 class="headline--primary">
{teaser.title} {teaser.title}
</h2> </h2>
<f:if condition="{date}"> <f:if condition="{travelData}">
<f:if condition="{teaser.category} == 'food'"> <f:if condition="{teaser.category} == 'food'">
<p> <f:if condition="{board -> f:count()} > 0">
{date.board} <f:then>
</p> <ul>
<f:for as="item" each="{board}">
<li>
{item.label}
</li>
</f:for>
</ul>
</f:then>
</f:if>
</f:if> </f:if>
<f:if condition="{teaser.category} == 'course'"> <f:if condition="{teaser.category} == 'course'">
<f:if condition="{date.activeCoursesForTravelinfo -> f:count()} > 0"> <f:if condition="{courses -> f:count()} > 0">
<f:then> <f:then>
<p> <p>
<strong>Folgende Kurse finden statt</strong> <strong>Folgende Kurse finden statt</strong>
</p> </p>
<ul> <ul>
<f:for as="course" each="{date.activeCoursesForTravelinfo}"> <f:for as="course" each="{courses}">
<li> <li>
{course.label} {course.label}
</li> </li>
@@ -257,7 +267,7 @@
<p> <p>
Bitte findet euch mindestens <strong>20 Minuten vor Abfahrt</strong> an der gebuchten Haltestelle ein. Bitte findet euch mindestens <strong>20 Minuten vor Abfahrt</strong> an der gebuchten Haltestelle ein.
</p> </p>
{travelinfo.busInfoText -> f:format.html()} {travelInfo.busInfoText -> f:format.html()}
<table class="w-full bg-zinc-200 text-zinc-800 text-xs md:text-base mb-4"> <table class="w-full bg-zinc-200 text-zinc-800 text-xs md:text-base mb-4">
<tr> <tr>
<th class="border-r border-white/50 px-2 py-1 md:p-2 text-left text-sm md:text-base"> <th class="border-r border-white/50 px-2 py-1 md:p-2 text-left text-sm md:text-base">
@@ -267,18 +277,23 @@
Datum/Uhrzeit Datum/Uhrzeit
</th> </th>
</tr> </tr>
<f:for each="{date.pickups}" as="pickup"> <f:for each="{travelData.pickupsTo}" as="pickup">
<tr class="odd:bg-zinc-50 align-top"> <tr class="odd:bg-zinc-50 align-top">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2"> <td class="border-r border-zinc-200 px-2 py-1 md:p-2">
{pickup.city}<br><span class="text-xs">{pickup.street}</span> {pickup.city}<br><span class="text-xs">{pickup.street}</span>
</td> </td>
<td class="border-r border-zinc-200 px-2 py-1 md:p-2"> <td class="border-r border-zinc-200 px-2 py-1 md:p-2">
{pickup.date}<br>{f:if(condition: pickup.time, then: '{pickup.time} Uhr', else: '???')} {pickup.time -> f:format.date(format: 'd.m.Y')}<br>{pickup.time -> f:format.date(format: 'H:i')} Uhr
</td> </td>
</tr> </tr>
</f:for> </f:for>
</table> </table>
{travelinfo.departureText -> f:format.html()} <f:if condition="{departureDayTime}">
<p>
Die Rückreise mit dem Bus startet <strong>{departureDayTime}</strong>.
</p>
</f:if>
{travelInfo.departureText -> f:format.html()}
</f:section> </f:section>
<f:section name="Icon"> <f:section name="Icon">
@@ -58,8 +58,7 @@
data-daytrip-type-param="services" data-daytrip-type-param="services"
data-daytrip-uid-param="{iteration.cycle}" data-daytrip-uid-param="{iteration.cycle}"
data-cooltipz-dir="top" data-cooltipz-dir="top"
aria-label="Alle Inklusivleistungen anzeigen" aria-label="Alle Inklusivleistungen anzeigen">
role="tooltip">
<svg class="w-4 h-4"> <svg class="w-4 h-4">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-info-circle"></use> <use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-info-circle"></use>
</svg> </svg>
@@ -20,12 +20,12 @@
<f:section name="Table"> <f:section name="Table">
<div class="w-full max-w-full overflow-x-scroll md:overflow-x-auto"> <div class="w-full max-w-full overflow-x-scroll md:overflow-x-auto">
<table class="min-w-full border border-zinc-200 border-collapse mb-8"> <table class="min-w-full border border-zinc-200 border-collapse mb-2">
<f:for each="{categories}" as="category" key="categoryKey"> <f:for each="{categories}" as="category" key="categoryKey">
<f:if condition="{v:variable.get(name: 'pricetable.{categoryKey}', useRawKeys: 1) -> f:count()} > 0"> <f:if condition="{v:variable.get(name: 'pricetable.{categoryKey}', useRawKeys: 1) -> f:count()} > 0">
<tr class="text-white {ep:themeClasses(classes: '{sbw: \'bg-sbw-primary\', snz: \'bg-snz-primary\', uch: \'bg-uch-primary\'}', key: themekey)}"> <tr class="text-white {ep:themeClasses(classes: '{sbw: \'bg-sbw-primary\', snz: \'bg-snz-primary\', uch: \'bg-uch-primary\'}', key: themekey)}">
<td class="px-2 py-1 font-bold border border-zinc-200"> <td class="px-2 py-1 font-bold border border-zinc-200">
Apartments {category} {category}
</td> </td>
<f:for each="{roomTypesLabels}" as="type"> <f:for each="{roomTypesLabels}" as="type">
<td class="px-2 py-1 border border-zinc-200"> <td class="px-2 py-1 border border-zinc-200">
@@ -36,6 +36,7 @@
<f:for each="{pricetable.{categoryKey}}" as="row"> <f:for each="{pricetable.{categoryKey}}" as="row">
<tr> <tr>
<td class="px-2 py-1 border border-zinc-200"> <td class="px-2 py-1 border border-zinc-200">
<f:link.typolink parameter="{row.hotelUri}" class="underline">
<f:if condition="{row.hotelShortName}"> <f:if condition="{row.hotelShortName}">
<f:then> <f:then>
{row.hotelShortName} {row.hotelShortName}
@@ -51,18 +52,18 @@
</f:if> </f:if>
</f:else> </f:else>
</f:if> </f:if>
</f:link.typolink>
</td> </td>
<f:for each="{roomTypesPax}" as="type"> <f:for each="{roomTypesPax}" as="type">
<td class="px-2 py-1 border border-zinc-200"> <td class="px-2 py-1 border border-zinc-200">
<f:if condition="{row.rooms.{type}.available}"> <f:if condition="{row.rooms.{type}.available}">
<f:then> <f:then>
<a class="{ep:themeClasses(classes: '{sbw: \'text-sbw-primary\', snz: \'text-snz-primary\', uch: \'text-uch-primary\'}', key: settings.themekey)}" <f:link.typolink
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-action="datalayer#trigger" parameter="{row.hotelUri}"
href="{row.bookingUrl}" class="whitespace-nowrap {ep:themeClasses(classes: '{sbw: \'text-sbw-primary\', snz: \'text-snz-primary\', uch: \'text-uch-primary\'}', key: settings.themekey)}">
title="Jetzt buchen"
target="_blank">
{row.rooms.{type}.price} {row.rooms.{type}.price}
</a> {f:if(condition: '{row.rooms.{type}.undersubscription}', then: '*')}
</f:link.typolink>
</f:then> </f:then>
<f:else> <f:else>
<f:if condition="{row.rooms.{type}.available}"> <f:if condition="{row.rooms.{type}.available}">
@@ -79,6 +80,7 @@
</f:if> </f:if>
</f:for> </f:for>
</table> </table>
<p class="text-sm mb-8">* Preis kommt durch eine Unterbelegung zustande.</p>
</div> </div>
</f:section> </f:section>
@@ -47,129 +47,17 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<f:for each="{prices}" as="row"> <f:comment><!--
<tr class="odd:bg-zinc-50 hover:bg-zinc-100 odd:hover:bg-zinc-100" Render available and unavailable rooms separately to maintain order:
data-pricetable-target="row"> 1. by pax
<td class="border-r border-zinc-200 px-2 py-1 md:p-2"> 2. by price
<f:if condition="{row.showBookingButton} && {row.available}"> 3. by availability
<f:then> --></f:comment>
<a class="inline-block text-ep-primary-light text-xs sm:text-sm md:text-base leading-tight" <f:for each="{prices.available}" as="row">
href="{row.bookingUrl}" <f:render section="Row" arguments="{_all}"/>
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-datalayer-action="trigger" </f:for>
target="_blank" <f:for each="{prices.unavailable}" as="row">
title="{row.roomName}, {hotel.name}, {dateRange} buchen"> <f:render section="Row" arguments="{_all}"/>
{row.roomName}
</a>
</f:then>
<f:else>
<span class="inline-block text-xs sm:text-sm md:text-base line-through leading-tight">
{row.roomName}
</span>
<f:if condition="{row.available}">
<f:else>
<span class="inline-block text-xs sm:text-sm md:text-base text-sm leading-tight lg:ml-2">ausgebucht</span>
</f:else>
</f:if>
</f:else>
</f:if>
</td>
<f:if condition="{hasIncludedServices}">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
<div class="flex items-center justify-around space-x-1">
<div class="grid grid-cols-2">
<f:if condition="{row.busIncluded}">
<f:then>
<span class="block p-1"
data-cooltipz-dir="top"
aria-label="Busfahrt inklusive"
role="tooltip"
data-pricetable-target="busIcon"
data-bus-included="{row.busIncluded}"
data-bus-price="{row.busPrice}">
<svg class="w-4 h-4 md:w-5 md:h-5">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-bus"></use>
</svg>
</span>
</f:then>
<f:else>
<span class="block"></span>
</f:else>
</f:if>
<f:if condition="{row.skipassIncluded}">
<f:then>
<span class="block p-1"
data-cooltipz-dir="top"
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
role="tooltip">
<svg class="w-4 h-4 md:w-5 md:h-5">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-tag"></use>
</svg>
</span>
</f:then>
<f:else>
<span class="block"></span>
</f:else>
</f:if>
</div>
</div>
</td>
</f:if>
<td class="border-r border-zinc-200 px-2 py-1 md:p-2 text-center">
<f:if condition="{row.hasOptionalServices} && {row.available}">
<button type="button"
class="text-ep-primary-light text-sm md:text-base text-left leading-none p-1"
data-action="pricetable#openModal"
data-pricetable-type-param="options"
data-pricetable-uid-param="{row.roomUid}"
data-cooltipz-dir="top"
aria-label="Alle Zusatzleistungen anzeigen"
role="tooltip">
Zusatz&shy;leistungen
</button>
</f:if>
</td>
<td class="px-2 py-1 md:p-2 whitespace-nowrap">
<f:if condition="{row.showBookingButton}">
<f:then>
<f:if condition="{row.available}">
<f:then>
<a class="button bg-button button--small w-full"
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-action="datalayer#trigger"
href="{row.bookingUrl}"
title="{row.roomName}, {hotel.name}, {dateRange} buchen"
target="_blank">
<span class="font-bold normal-case whitespace-nowrap"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice}
</span>
</a>
</f:then>
<f:else>
<span class="button bg-button bg-button--muted button--small block cursor-not-allowed font-bold normal-case whitespace-nowrap"
data-cooltipz-dir="top"
aria-label="Leider ausgebucht"
role="tooltip"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice} €
</span>
</f:else>
</f:if>
</f:then>
<f:else>
<f:link.typolink parameter="{settings.travelAlertPageUid}"
class="button bg-button button--small">
Reisen-Alert
</f:link.typolink>
</f:else>
</f:if>
</td>
</tr>
</f:for> </f:for>
</tbody> </tbody>
</table> </table>
@@ -277,17 +165,148 @@
</button> </button>
</div> </div>
<div class="h-128 p-4 overflow-y-scroll bg-white rounded-b"> <div class="h-128 p-4 overflow-y-scroll bg-white rounded-b">
<f:for each="{prices}" as="row"> <f:for each="{prices.available}" as="row">
<f:render section="OptionsModalRow" arguments="{_all}"/>
</f:for>
</div>
</div>
</div>
</f:section>
<f:section name="Row">
<tr class="odd:bg-zinc-50 hover:bg-zinc-100 odd:hover:bg-zinc-100"
data-pricetable-target="row">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
<f:if condition="{row.showBookingButton} && {row.available}">
<f:then>
<a class="inline-block text-ep-primary-light text-xs sm:text-sm md:text-base leading-tight"
href="{row.bookingUrl}"
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-datalayer-action="trigger"
target="_blank"
title="{row.roomName}, {hotel.name}, {dateRange} buchen">
{row.roomName}
</a>
</f:then>
<f:else>
<span class="inline-block text-xs sm:text-sm md:text-base line-through leading-tight">{row.roomName}</span>
<f:if condition="{row.available}">
<f:else>
<span class="inline-block text-xs sm:text-sm md:text-base text-sm leading-tight lg:ml-2">ausgebucht</span>
</f:else>
</f:if>
</f:else>
</f:if>
</td>
<f:if condition="{hasIncludedServices}">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
<div class="flex items-center justify-around space-x-1">
<div class="grid grid-cols-2">
<f:if condition="{row.busIncluded}">
<f:then>
<span class="block p-1"
data-cooltipz-dir="top"
aria-label="Busfahrt inklusive"
role="tooltip"
data-pricetable-target="busIcon"
data-bus-included="{row.busIncluded}"
data-bus-price="{row.busPrice}">
<svg class="w-4 h-4 md:w-5 md:h-5">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-bus"></use>
</svg>
</span>
</f:then>
<f:else>
<span class="block"></span>
</f:else>
</f:if>
<f:if condition="{row.skipassIncluded}">
<f:then>
<span class="block p-1"
data-cooltipz-dir="top"
aria-label="{f:if(condition: row.summer, then: 'Bergbahnticket inklusive', else: 'Skipass inklusive')}"
role="tooltip">
<svg class="w-4 h-4 md:w-5 md:h-5">
<use href="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/icons_sprite.svg')}#icon-tag"></use>
</svg>
</span>
</f:then>
<f:else>
<span class="block"></span>
</f:else>
</f:if>
</div>
</div>
</td>
</f:if>
<td class="border-r border-zinc-200 px-2 py-1 md:p-2 text-center">
<f:if condition="{row.hasOptionalServices} && {row.available}">
<button type="button"
class="text-ep-primary-light text-sm md:text-base text-left leading-none p-1"
data-action="pricetable#openModal"
data-pricetable-type-param="options"
data-pricetable-uid-param="{row.roomUid}"
data-cooltipz-dir="top"
aria-label="Alle Zusatzleistungen anzeigen">
Zusatz&shy;leistungen
</button>
</f:if>
</td>
<td class="px-2 py-1 md:p-2 whitespace-nowrap">
<f:if condition="{row.showBookingButton}">
<f:then>
<f:if condition="{row.available}">
<f:then>
<a class="button bg-button button--small w-full"
data-controller="datalayer" data-datalayer-event-value="ZurBuchung" data-action="datalayer#trigger"
href="{row.bookingUrl}"
title="{row.roomName}, {hotel.name}, {dateRange} buchen"
target="_blank">
<span class="font-bold normal-case whitespace-nowrap"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice}
</span>
</a>
</f:then>
<f:else>
<span class="button bg-button bg-button--muted button--small block cursor-not-allowed font-bold normal-case whitespace-nowrap"
data-cooltipz-dir="top"
aria-label="Leider ausgebucht"
role="tooltip"
data-pricetable-target="price"
data-room="{row.minPrice}"
data-bus="{row.busPrice}"
data-discount="{row.discount}">
{row.minPrice} €
</span>
</f:else>
</f:if>
</f:then>
<f:else>
<f:link.typolink parameter="{settings.travelAlertPageUid}"
class="button bg-button button--small">
Reisen-Alert
</f:link.typolink>
</f:else>
</f:if>
</td>
</tr>
</f:section>
<f:Section name="OptionsModalRow">
<div class="hidden" <div class="hidden"
data-modal-target="content" data-modal-target="content"
data-uid="{row.roomUid}"> data-uid="{row.roomUid}">
<f:for each="{row.optionalServices}" as="section" key="sectionCode"> <f:for each="{row.optionalServices}" as="section" key="sectionCode">
<f:if condition="{section.services}">
<div class="py-2 -mx-1"> <div class="py-2 -mx-1">
<div class="font-bold text-xl px-1"> <div class="font-bold text-xl px-1">
{section.label} {section.label}
</div> </div>
<f:for each="{section.services}" as="service"> <f:for each="{section.services}" as="service">
<f:if condition="{sectionCode} == 'BUS' || {service.price} != '0,00'"> <f:if condition="{sectionCode} == 'BUS' || {sectionCode} == 'VPF' || {service.price} != '0,00'">
<dl class="flex items-start justify-between p-1 hover:bg-zinc-50"> <dl class="flex items-start justify-between p-1 hover:bg-zinc-50">
<dt class="font-normal"> <dt class="font-normal">
{service.label}: {service.label}:
@@ -299,12 +318,8 @@
</f:if> </f:if>
</f:for> </f:for>
</div> </div>
</f:if>
</f:for> </f:for>
</div> </div>
</f:for> </f:Section>
</div>
</div>
</div>
</f:section>
</html> </html>
@@ -13,7 +13,7 @@
<h1>Anfrage Gruppenhaus vom <f:format.date date="now" format="d.m.y" /></h1> <h1>Anfrage Gruppenhaus vom <f:format.date date="now" format="d.m.y" /></h1>
</f:else> </f:else>
</f:if> </f:if>
<table> <table class="data">
<tr> <tr>
<th> <th>
Art Art
@@ -35,8 +35,16 @@
<td>{group}</td> <td>{group}</td>
</tr> </tr>
<tr> <tr>
<th>Adresse</th> <th>Straße</th>
<td>{address -> f:format.nl2br()}</td> <td>{street}</td>
</tr>
<tr>
<th>Postleitzahl</th>
<td>{postcode}</td>
</tr>
<tr>
<th>Ort</th>
<td>{city}</td>
</tr> </tr>
<tr> <tr>
<th>E-Mail</th> <th>E-Mail</th>
@@ -50,10 +58,28 @@
<th>Zeitraum</th> <th>Zeitraum</th>
<td>{dateFrom} - {dateTo}</td> <td>{dateFrom} - {dateTo}</td>
</tr> </tr>
<tr>
<th>Nächte</th>
<td>{nights}</td>
</tr>
<tr> <tr>
<th>Anzahl der Personen</th> <th>Anzahl der Personen</th>
<td>{pax -> v:or(alternative: '-')}</td> <td>{pax -> v:or(alternative: '-')}</td>
</tr> </tr>
<tr>
<th>Anzahl Kinder 0-3 Jahre</th>
<td>{children -> v:or(alternative: '-')}</td>
</tr>
<tr>
<th>Anzahl Kinder 4-5 Jahre</th>
<td>{minors -> v:or(alternative: '-')}</td>
</tr>
<f:if condition="{adolescentsAge}">
<tr>
<th>Anzahl Kinder 6-{adolescentsAge} Jahre</th>
<td>{adolescents -> v:or(alternative: '-')}</td>
</tr>
</f:if>
<tr> <tr>
<th>Verpflegung</th> <th>Verpflegung</th>
<td> <td>
@@ -78,25 +104,52 @@
</f:for> </f:for>
</ul> </ul>
</f:then> </f:then>
<f:else>
-
</f:else>
</f:if> </f:if>
</td> </td>
</tr> </tr>
<tr> <tr>
<th>Kostenübersicht</th> <th>Kostenübersicht</th>
<td> <td>
Grundpreis: {summary.paxBase}<br> <ul>
Aufpreis Personen: {summary.paxAdditional -> v:or(alternative: '-')}<br> <li>
Verpflegung: {summary.board -> v:or(alternative: '-')}<br> Grundpreis: {summary.paxBase}
Kleingruppen-Verpflegungszuschlag: {summary.undersubscription -> v:or(alternative: '-')}<br> </li>
Aufpreis Kurzzeit: {summary.shortTerm -> v:or(alternative: '-')}<br> <li>
Zusatzleistungen: {summary.options}<br> Aufpreis Personen: {summary.paxAdditional -> v:or(alternative: '-')}
Strom- und Abfallgebühren: {summary.runningCosts -> v:or(alternative: '-')}<br> </li>
<li>
Verpflegung: {summary.board -> v:or(alternative: '-')}
</li>
<li>
Kleingruppen-Verpflegungszuschlag: {summary.undersubscription -> v:or(alternative: '-')}
</li>
<li>
Aufpreis Kurzzeit: {summary.shortTerm -> v:or(alternative: '-')}
</li>
<li>
Strom- und Abfallgebühren: {summary.runningCosts -> v:or(alternative: '-')}
</li>
<li>
<strong>Gesamtpreis: {summary.total}</strong> <strong>Gesamtpreis: {summary.total}</strong>
</li>
</ul>
</td> </td>
</tr> </tr>
<tr> <tr>
<th>Bemerkungen/Wünsche</th> <th>Bemerkungen/Wünsche</th>
<td>{remarks -> f:format.nl2br()}</td> <td>
<f:if condition="{remarks}">
<f:then>
{remarks -> f:format.nl2br()}
</f:then>
<f:else>
-
</f:else>
</f:if>
</td>
</tr> </tr>
</table> </table>
</f:section> </f:section>
@@ -15,10 +15,10 @@
options-json='{options}' options-json='{options}'
running-costs-factor-eur="{runningCostsEUR}" running-costs-factor-eur="{runningCostsEUR}"
running-costs-factor-chf="{runningCostsCHF}" running-costs-factor-chf="{runningCostsCHF}"
undersubscription-eur="{undersubscriptionEUR}" undersubscription-30-eur="{undersubscription30EUR}"
undersubscription-chf="{undersubscriptionCHF}" undersubscription-30-chf="{undersubscription30CHF}"
undersubscription-ext-eur="{undersubscriptionExtEUR}" undersubscription-40-eur="{undersubscription40EUR}"
undersubscription-ext-chf="{undersubscriptionExtCHF}" undersubscription-40-chf="{undersubscription40CHF}"
country-code="{countryCode}" country-code="{countryCode}"
endpoint="{ep:uri.ajax(action: 'processForm', controller: 'AjaxGroupsPrice', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}" endpoint="{ep:uri.ajax(action: 'processForm', controller: 'AjaxGroupsPrice', pageUid: settings.defaultAjaxUid, arguments: '{hotel: hotel}')}"
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}" loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
@@ -26,6 +26,8 @@
logo-at="{f:uri.image(src: 'fileadmin/user_upload/Logos_Banner/Cllt_Touristik_frei_gestellt.jpg')}" logo-at="{f:uri.image(src: 'fileadmin/user_upload/Logos_Banner/Cllt_Touristik_frei_gestellt.jpg')}"
main-season-from="{mainSeasonFrom}" main-season-from="{mainSeasonFrom}"
main-season-to="{mainSeasonTo}" main-season-to="{mainSeasonTo}"
tax-label="{hotel.groupsPriceTaxLabel -> v:or(alternative: 'zzgl. vor Ort zu entrichtender Ortstaxe')}"
adolescents-age="{adolescentsAge}"
> >
</groups-price-calculator> </groups-price-calculator>
</f:format.raw> </f:format.raw>
@@ -339,7 +339,7 @@
data-lightbox-type-value="external" data-lightbox-type-value="external"
data-lightbox-height-value="85vh" data-lightbox-height-value="85vh"
data-action="lightbox#open"> data-action="lightbox#open">
Preisberechnung &amp; direkt buchen Preisrechner/Buchungstool
</button> </button>
</f:if> </f:if>
<div class="bg-zinc-50 p-4 flex flex-col space-y-2 relative mb-8"> <div class="bg-zinc-50 p-4 flex flex-col space-y-2 relative mb-8">