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'); }
private function handleRequest(string $travelCode, Travelinfo $travelInfo)
{
// fetch travel data via API call
try {
$travelData = $this->apiClient->getTravel($travelCode);
} catch (ApiException $e) {
$travelData = [];
} }
/** @var Date $date */ // extract courses from additional services
$date = $this->dateRepository->findForProductAndDate($product, $travelDate)->getFirst(); $courses = array_filter($travelData['additionalServices'] ?? [], function ($service) {
return 'KUR' === $service['subType'];
});
$this->view->assign('travelinfo', $travelinfo); // extract board services from additional services
$this->view->assign('date', $date); $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,18 +207,11 @@ 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')
->addOrderBy('room.price', 'ASC')
->addOrderBy('room.available', 'DESC')
; ;
if (true === $sortByRoom) {
$qb->addOrderBy('room.name', 'ASC');
} else {
$qb
->addOrderBy('room.pax', 'ASC')
->addOrderBy('room.price', 'ASC')
;
}
$qb $qb
->where('date.date_start >= NOW()') ->where('date.date_start >= NOW()')
->andWhere($qb->expr()->eq( ->andWhere($qb->expr()->eq(
@@ -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',
] ]
); );
+64 -62
View File
@@ -213,72 +213,74 @@ CREATE TABLE tx_epproducts_domain_model_date
CREATE TABLE tx_epproducts_domain_model_hotel CREATE TABLE tx_epproducts_domain_model_hotel
( (
uid int(11) NOT NULL auto_increment, uid int(11) NOT NULL auto_increment,
pid int(11) DEFAULT '0' NOT NULL, pid int(11) DEFAULT '0' NOT NULL,
earlybird tinyint(4) unsigned DEFAULT '0' NOT NULL, earlybird tinyint(4) unsigned DEFAULT '0' NOT NULL,
new tinyint(4) unsigned DEFAULT '0' NOT NULL, new tinyint(4) unsigned DEFAULT '0' NOT NULL,
low_contingent tinyint(4) unsigned DEFAULT '0' NOT NULL, low_contingent tinyint(4) unsigned DEFAULT '0' NOT NULL,
show_as_teaser tinyint(4) unsigned DEFAULT '0' NOT NULL, show_as_teaser tinyint(4) unsigned DEFAULT '0' NOT NULL,
detail_page varchar(255) DEFAULT '' NOT NULL, detail_page varchar(255) DEFAULT '' NOT NULL,
name varchar(255) DEFAULT '' NOT NULL, name varchar(255) DEFAULT '' NOT NULL,
short_name varchar(255) DEFAULT '' NOT NULL, short_name varchar(255) DEFAULT '' NOT NULL,
headline varchar(255) DEFAULT '' NOT NULL, headline varchar(255) DEFAULT '' NOT NULL,
description text NOT NULL, description text NOT NULL,
keywords text NOT NULL, keywords text NOT NULL,
features text NOT NULL, features text NOT NULL,
room_types text NOT NULL, room_types text NOT NULL,
additional_information text NOT NULL, additional_information text NOT NULL,
teaser text NOT NULL, teaser text NOT NULL,
header_title varchar(255) DEFAULT '' NOT NULL, header_title varchar(255) DEFAULT '' NOT NULL,
header_subtitle varchar(255) DEFAULT '' NOT NULL, header_subtitle varchar(255) DEFAULT '' NOT NULL,
teaser_images int(11) unsigned NOT NULL default '0', teaser_images int(11) unsigned NOT NULL default '0',
header_images int(11) unsigned NOT NULL default '0', header_images int(11) unsigned NOT NULL default '0',
images int(11) unsigned NOT NULL default '0', images int(11) unsigned NOT NULL default '0',
reseller_images int(11) unsigned NOT NULL default '0', reseller_images int(11) unsigned NOT NULL default '0',
code varchar(255) DEFAULT '' NOT NULL, code varchar(255) DEFAULT '' NOT NULL,
external_link varchar(255) DEFAULT '' NOT NULL, external_link varchar(255) DEFAULT '' NOT NULL,
path_segment varchar(255) DEFAULT '' NOT NULL, path_segment varchar(255) DEFAULT '' NOT NULL,
type varchar(255) DEFAULT '' NOT NULL, type varchar(255) DEFAULT '' NOT NULL,
category int(11) DEFAULT '0' NOT NULL, category int(11) DEFAULT '0' NOT NULL,
country int(11) unsigned DEFAULT '0', country int(11) unsigned DEFAULT '0',
region int(11) unsigned DEFAULT '0', region int(11) unsigned DEFAULT '0',
city int(11) unsigned DEFAULT '0', city int(11) unsigned DEFAULT '0',
teamer int(11) unsigned DEFAULT '0' NOT NULL, teamer int(11) unsigned DEFAULT '0' NOT NULL,
products int(11) unsigned DEFAULT '0' NOT NULL, products int(11) unsigned DEFAULT '0' NOT NULL,
rooms int(11) unsigned DEFAULT '0' NOT NULL, rooms int(11) unsigned DEFAULT '0' NOT NULL,
facts int(11) unsigned DEFAULT '0' NOT NULL, facts int(11) unsigned DEFAULT '0' NOT NULL,
additional_codes int(11) unsigned DEFAULT '0' NOT NULL, additional_codes int(11) unsigned DEFAULT '0' NOT NULL,
address varchar(255) DEFAULT '' NOT NULL, address varchar(255) DEFAULT '' NOT NULL,
latitude varchar(255) DEFAULT '' NOT NULL, latitude varchar(255) DEFAULT '' NOT NULL,
longitude varchar(255) DEFAULT '' NOT NULL, longitude varchar(255) DEFAULT '' NOT NULL,
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',
main_season_from date DEFAULT NULL, groups_price_adolescents_age int(11) unsigned NOT NULL default '0',
main_season_to date DEFAULT NULL, groups_price_tax_label varchar(255) DEFAULT '' NOT NULL,
main_season_from date DEFAULT NULL,
main_season_to date DEFAULT NULL,
tstamp int(11) unsigned DEFAULT '0' NOT NULL, tstamp int(11) unsigned DEFAULT '0' NOT NULL,
crdate int(11) unsigned DEFAULT '0' NOT NULL, crdate int(11) unsigned DEFAULT '0' NOT NULL,
cruser_id int(11) unsigned DEFAULT '0' NOT NULL, cruser_id int(11) unsigned DEFAULT '0' NOT NULL,
deleted tinyint(4) unsigned DEFAULT '0' NOT NULL, deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
hidden tinyint(4) unsigned DEFAULT '0' NOT NULL, hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
starttime int(11) unsigned DEFAULT '0' NOT NULL, starttime int(11) unsigned DEFAULT '0' NOT NULL,
endtime int(11) unsigned DEFAULT '0' NOT NULL, endtime int(11) unsigned DEFAULT '0' NOT NULL,
t3ver_oid int(11) DEFAULT '0' NOT NULL, t3ver_oid int(11) DEFAULT '0' NOT NULL,
t3ver_id int(11) DEFAULT '0' NOT NULL, t3ver_id int(11) DEFAULT '0' NOT NULL,
t3ver_wsid int(11) DEFAULT '0' NOT NULL, t3ver_wsid int(11) DEFAULT '0' NOT NULL,
t3ver_label varchar(255) DEFAULT '' NOT NULL, t3ver_label varchar(255) DEFAULT '' NOT NULL,
t3ver_state tinyint(4) DEFAULT '0' NOT NULL, t3ver_state tinyint(4) DEFAULT '0' NOT NULL,
t3ver_stage int(11) DEFAULT '0' NOT NULL, t3ver_stage int(11) DEFAULT '0' NOT NULL,
t3ver_count int(11) DEFAULT '0' NOT NULL, t3ver_count int(11) DEFAULT '0' NOT NULL,
t3ver_tstamp int(11) DEFAULT '0' NOT NULL, t3ver_tstamp int(11) DEFAULT '0' NOT NULL,
t3ver_move_id int(11) DEFAULT '0' NOT NULL, t3ver_move_id int(11) DEFAULT '0' NOT NULL,
sys_language_uid int(11) DEFAULT '0' NOT NULL, sys_language_uid int(11) DEFAULT '0' NOT NULL,
l10n_parent int(11) DEFAULT '0' NOT NULL, l10n_parent int(11) DEFAULT '0' NOT NULL,
l10n_diffsource mediumblob, l10n_diffsource mediumblob,
PRIMARY KEY (uid), PRIMARY KEY (uid),
KEY parent (pid), KEY parent (pid),
@@ -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,20 +16,76 @@
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">
<label class="mb-2 font-bold"> <div>
Personenzahl <label class="mb-2 font-bold">
</label> Personenzahl
<input type="number" </label>
class="form-field" <input type="number"
v-model.number="selectedPax" class="form-field"
@change="onPaxUpdated()" min="30"
v-show="nightsCount > 0"/> v-model.number="selectedPax"
<p class="text-sm italic" @change="onPaxUpdated()"
v-show="nightsCount === 0"> v-show="nightsCount > 0"/>
Bitte zuerst einen Zeitraum wählen <p class="text-sm italic"
</p> v-show="nightsCount === 0">
Bitte zuerst einen Zeitraum wählen
</p>
</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>
<div class="mb-8" <div class="mb-8"
v-show="options.length > 0 && !submitted"> v-show="options.length > 0 && !submitted">
@@ -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>
<f:render section="Content"/> <main>
<f:render section="Teasers"/> <f:render section="Content"/>
<f:render partial="Footer" arguments="{_all}"/> <f:render section="Teasers"/>
<f:render partial="ContactBar" arguments="{_all}"/> </main>
<f:render partial="Topnav" arguments="{_all}"/> <footer>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> <f:render partial="Footer" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/> </footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" 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="Mainnav" section="Main" arguments="{_all}"/>
<f:render partial="HeaderSlider" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else>
</f:if>
<f:render section="Content"/>
<f:render partial="Footer" 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="Mainnav" section="Main" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <header>
<f:else> <f:render partial="HeaderSlider" arguments="{_all}"/>
<f:render partial="Search/MobileSearch" arguments="{_all}"/> </header>
</f:else> <main>
</f:if> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else>
<f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:else>
</f:if>
<f:render section="Content"/>
</main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:else>
<f:render partial="Search/MobileSearch" arguments="{_all}"/>
</f:else>
</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}"/>
<f:render partial="EventHero" arguments="{_all}"/> <header>
<f:render partial="EventDisturber" arguments="{headline: settings.eventDisturberHeadline, listItems: settings.eventDisturberListitems, buttonLabel: settings.eventDisturberButtonLabel, buttonLink: settings.eventDisturberButtonLink}"/> <f:render partial="EventHero" arguments="{_all}"/>
<f:render section="Content"/> <f:render partial="EventDisturber" arguments="{headline: settings.eventDisturberHeadline, listItems: settings.eventDisturberListitems, buttonLabel: settings.eventDisturberButtonLabel, buttonLink: settings.eventDisturberButtonLink}"/>
<f:render partial="Footer" arguments="{_all}"/> </header>
<f:render partial="ContactBar" arguments="{_all}"/> <main>
<f:render partial="Topnav" arguments="{_all}"/> <f:render section="Content"/>
<f:render partial="Mobilenav" section="Main" arguments="{_all}"/> </main>
<footer>
<f:render partial="Footer" arguments="{_all}"/>
</footer>
<aside>
<f:render partial="ContactBar" arguments="{_all}"/>
<f:render partial="Topnav" 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}"/>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <header>
<f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}"> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:else> <f:if condition="{data.tx_eptheme_hide_searchbar} || {settings.hideSearchbar}">
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:else>
</f:else> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:if> </f:else>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> </f:if>
<f:render section="Content"/> </header>
<f:render section="Teasers"/> <main>
<f:render partial="Footer" arguments="{_all}"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Content"/>
<f:render section="Teasers"/>
</main>
<footer>
<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,24 +3,30 @@
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}"/>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <header>
<f:if condition="{data.tx_eptheme_hide_searchbar}"> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:else> <f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:else>
</f:else> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:if> </f:else>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> </f:if>
<f:render section="Header" optional="true"/> </header>
<div class="container grid grid-cols-3 gap-8"> <main>
<div class="col-span-3 lg:col-span-1 order-last lg:order-none"> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Sidebar"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-1 order-last lg:order-none">
<f:render section="Sidebar"/>
</div>
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
</div> </div>
<div class="col-span-3 lg:col-span-2"> <f:render section="Teasers"/>
<f:render section="Content"/> </main>
</div> <footer>
</div> <f:render partial="Footer" arguments="{_all}"/>
<f:render section="Teasers"/> </footer>
<f:render partial="Footer" arguments="{_all}"/>
<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,19 +3,25 @@
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}"/>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <header>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:render section="Header" optional="true"/> </header>
<div class="container grid grid-cols-3 gap-8"> <main>
<div class="col-span-3 lg:col-span-1 order-last lg:order-none"> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Sidebar"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-1 order-last lg:order-none">
<f:render section="Sidebar"/>
</div>
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
</div> </div>
<div class="col-span-3 lg:col-span-2"> <f:render section="Teasers"/>
<f:render section="Content"/> </main>
</div> <footer>
</div> <f:render partial="Footer" arguments="{_all}"/>
<f:render section="Teasers"/> </footer>
<f:render partial="Footer" arguments="{_all}"/>
<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,24 +3,30 @@
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}"/>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <header>
<f:if condition="{data.tx_eptheme_hide_searchbar}"> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:else> <f:if condition="{data.tx_eptheme_hide_searchbar}">
<f:cObject typoscriptObjectPath="lib.searchBar"/> <f:else>
</f:else> <f:cObject typoscriptObjectPath="lib.searchBar"/>
</f:if> </f:else>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> </f:if>
<f:render section="Header" optional="true"/> </header>
<div class="container grid grid-cols-3 gap-8"> <main>
<div class="col-span-3 lg:col-span-2"> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<f:render section="Content"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
<div class="col-span-3 lg:col-span-1">
<f:render section="Sidebar"/>
</div>
</div> </div>
<div class="col-span-3 lg:col-span-1"> <f:render section="Teasers"/>
<f:render section="Sidebar"/> </main>
</div> <footer>
</div> <f:render partial="Footer" arguments="{_all}"/>
<f:render section="Teasers"/> </footer>
<f:render partial="Footer" arguments="{_all}"/>
<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,19 +3,25 @@
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}"/>
<f:render partial="HeaderSliderSmall" arguments="{_all}"/> <header>
<f:cObject typoscriptObjectPath="lib.breadcrumb"/> <f:render partial="HeaderSliderSmall" arguments="{_all}"/>
<f:render section="Header" optional="true"/> <f:cObject typoscriptObjectPath="lib.breadcrumb"/>
<div class="container grid grid-cols-3 gap-8"> </header>
<div class="col-span-3 lg:col-span-2"> <main>
<f:render section="Content"/> <f:render section="Header" optional="true"/>
<div class="container grid grid-cols-3 gap-8">
<div class="col-span-3 lg:col-span-2">
<f:render section="Content"/>
</div>
<div class="col-span-3 lg:col-span-1">
<f:render section="Sidebar"/>
</div>
</div> </div>
<div class="col-span-3 lg:col-span-1"> <f:render section="Teasers"/>
<f:render section="Sidebar"/> </main>
</div> <footer>
</div> <f:render partial="Footer" arguments="{_all}"/>
<f:render section="Teasers"/> </footer>
<f:render partial="Footer" arguments="{_all}"/>
<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>
<f:render section="Content"/> <main>
<f:render partial="Footer" arguments="{_all}"/> <f:render section="Content"/>
</main>
<footer>
<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,23 +4,27 @@
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>
<h3>
Eigenanreise
</h3>
{journey.byCar -> f:format.html()}
<f:if condition="{journey.image}">
<ep:lazyLoadImage image="{journey.image}"
loaderimg="EXT:ep_theme/Resources/Public/images/lazy.png"
class="block w-full h-auto"
width="770"
height="430c+100"
alt="{journey.image.alternative}" />
</f:if>
<f:if condition="{journey.byBus}"> <f:if condition="{journey.byBus}">
<h3> <h3>
Busanreise Busanreise
</h3> </h3>
{journey.byBus -> f:format.html()} <div class="pb-8">
{journey.byBus -> f:format.html()}
</div>
</f:if>
<f:if condition="{journey.byCar}">
<h3>
Eigenanreise
</h3>
{journey.byCar -> f:format.html()}
<f:if condition="{journey.image}">
<ep:lazyLoadImage image="{journey.image}"
loaderimg="EXT:ep_theme/Resources/Public/images/lazy.png"
class="block w-full h-auto"
width="770"
height="430c+100"
alt="{journey.image.alternative}" />
</f:if>
</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,33 +36,34 @@
<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:if condition="{row.hotelShortName}"> <f:link.typolink parameter="{row.hotelUri}" class="underline">
<f:then> <f:if condition="{row.hotelShortName}">
{row.hotelShortName} <f:then>
</f:then> {row.hotelShortName}
<f:else> </f:then>
<f:if condition="{row.hotelTitle}"> <f:else>
<f:then> <f:if condition="{row.hotelTitle}">
{row.hotelTitle} <f:then>
</f:then> {row.hotelTitle}
<f:else> </f:then>
{row.hotelName} <f:else>
</f:else> {row.hotelName}
</f:if> </f:else>
</f:else> </f:if>
</f:if> </f:else>
</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,34 +165,161 @@
</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">
<div class="hidden" <f:render section="OptionsModalRow" arguments="{_all}"/>
data-modal-target="content"
data-uid="{row.roomUid}">
<f:for each="{row.optionalServices}" as="section" key="sectionCode">
<div class="py-2 -mx-1">
<div class="font-bold text-xl px-1">
{section.label}
</div>
<f:for each="{section.services}" as="service">
<f:if condition="{sectionCode} == 'BUS' || {service.price} != '0,00'">
<dl class="flex items-start justify-between p-1 hover:bg-zinc-50">
<dt class="font-normal">
{service.label}:
</dt>
<dd class="whitespace-nowrap">
{service.price} €
</dd>
</dl>
</f:if>
</f:for>
</div>
</f:for>
</div>
</f:for> </f:for>
</div> </div>
</div> </div>
</div> </div>
</f:section> </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"
data-modal-target="content"
data-uid="{row.roomUid}">
<f:for each="{row.optionalServices}" as="section" key="sectionCode">
<f:if condition="{section.services}">
<div class="py-2 -mx-1">
<div class="font-bold text-xl px-1">
{section.label}
</div>
<f:for each="{section.services}" as="service">
<f:if condition="{sectionCode} == 'BUS' || {sectionCode} == 'VPF' || {service.price} != '0,00'">
<dl class="flex items-start justify-between p-1 hover:bg-zinc-50">
<dt class="font-normal">
{service.label}:
</dt>
<dd class="whitespace-nowrap">
{service.price} €
</dd>
</dl>
</f:if>
</f:for>
</div>
</f:if>
</f:for>
</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>
<strong>Gesamtpreis: {summary.total}</strong> <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>
</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">
@@ -6,6 +6,6 @@
<f:section name="Main"> <f:section name="Main">
<f:render partial="Travelinfo" section="Main" arguments="{_all}"/> <f:render partial="Travelinfo" section="Main" arguments="{_all}"/>
</f:section> </f:section>
</html> </html>