diff --git a/admin-dev/themes/default/template/controllers/orders/_product_line.tpl b/admin-dev/themes/default/template/controllers/orders/_product_line.tpl
index 17cf1903f..493123c86 100644
--- a/admin-dev/themes/default/template/controllers/orders/_product_line.tpl
+++ b/admin-dev/themes/default/template/controllers/orders/_product_line.tpl
@@ -147,7 +147,7 @@
{if ($can_edit)}
- {if isset($refundReqBookings) && $refundReqBookings && $data.id|in_array:$refundReqBookings && ($data.id_status == $ROOM_STATUS_CANCELLED || $data.id_status == $ROOM_STATUS_NO_SHOW)}
+ {if $data.id_status == $ROOM_STATUS_CANCELLED || $data.id_status == $ROOM_STATUS_NO_SHOW}
{l s='Delete'}
diff --git a/admin-dev/themes/default/template/helpers/form/form_group.tpl b/admin-dev/themes/default/template/helpers/form/form_group.tpl
index 580df3844..089af5a94 100644
--- a/admin-dev/themes/default/template/helpers/form/form_group.tpl
+++ b/admin-dev/themes/default/template/helpers/form/form_group.tpl
@@ -64,7 +64,7 @@
{else}
-
+
{l s='No group created'}
{/if}
\ No newline at end of file
diff --git a/classes/order/OrderDetail.php b/classes/order/OrderDetail.php
index 96f932f3a..29ba8750c 100644
--- a/classes/order/OrderDetail.php
+++ b/classes/order/OrderDetail.php
@@ -503,7 +503,7 @@ class OrderDetailCore extends ObjectModel
$this->product_price_calculation_method,
$cartServiceItem['date_from'],
$cartServiceItem['date_to']
- );
+ );
$itemQuantity = (isset($cartServiceItem['quantity']) ? $cartServiceItem['quantity'] : 0) * $itemNumDays;
$quantity += $itemQuantity;
$totalPriceTaxExcl += $itemQuantity * (isset($cartServiceItem['unit_price_tax_excl']) ? $cartServiceItem['unit_price_tax_excl'] : 0);
@@ -527,7 +527,7 @@ class OrderDetailCore extends ObjectModel
if (isset($quantity) && isset($unit_price_tax_excl)) {
foreach ($this->tax_calculator->getTaxesAmount($unit_price_tax_excl) as $id_tax => $amount) {
- $total_amount = Tools::processPriceRounding($amount, $quantity, $order->round_type, $order->round_mode);
+ $total_amount = $amount * $quantity;
$values .= '('.(int)$order->id.','.(int)$this->id.',0,0,'.(int)$id_tax.','.(float)$amount.','.(float)$total_amount.',NOW()),';
}
@@ -561,7 +561,7 @@ class OrderDetailCore extends ObjectModel
foreach ($this->tax_calculator->getTaxesAmount($this->unit_price_tax_excl) as $id_tax => $amount) {
- $total_amount = Tools::processPriceRounding($amount, $this->product_quantity, $order->round_type, $order->round_mode);
+ $total_amount = $amount * $this->product_quantity;
$values .= '('.(int)$order->id.','.(int)$this->id.',0,0,'.(int)$id_tax.','.(float)$amount.','.(float)$total_amount.',NOW()),';
}
diff --git a/controllers/front/CategoryController.php b/controllers/front/CategoryController.php
index d98e78bbf..80ce4ded5 100644
--- a/controllers/front/CategoryController.php
+++ b/controllers/front/CategoryController.php
@@ -223,6 +223,28 @@ class CategoryControllerCore extends FrontController
if ($booking_data = $objBookingDetail->dataForFrontSearch($bookingParams)) {
$booking_data['rm_data'] = array_values($booking_data['rm_data']);
+ $maxAmenities = (int) Configuration::get('WK_MAX_AMENITIES_ROOM_SEARCH');
+ $maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_ROOM_SEARCH');
+ $maxFeatureValues = (int) Configuration::get('WK_MAX_ROOM_TYPE_FEATURE_VALUES');
+ foreach ($booking_data['rm_data'] as &$roomData) {
+ if ($maxAmenities && !empty($roomData['featured_amenities'])) {
+ $roomData['featured_amenities'] = array_slice($roomData['featured_amenities'], 0, $maxAmenities);
+ }
+ if (!empty($roomData['featured_features'])) {
+ if ($maxFeatures) {
+ $roomData['featured_features'] = array_slice($roomData['featured_features'], 0, $maxFeatures);
+ }
+ foreach ($roomData['featured_features'] as &$featuredFeature) {
+ if ($maxFeatureValues) {
+ $featuredFeature['values'] = array_slice($featuredFeature['values'], 0, $maxFeatureValues);
+ }
+ $featuredFeature['value'] = implode(', ', $featuredFeature['values']);
+ }
+ unset($featuredFeature);
+ }
+ }
+ unset($roomData);
+
if ($sort_by && $sort_value) {
$indi_arr = array();
diff --git a/install-dev/models/install.php b/install-dev/models/install.php
index dfc4df2ea..d9dabfca6 100644
--- a/install-dev/models/install.php
+++ b/install-dev/models/install.php
@@ -673,9 +673,9 @@ class InstallModelInstall extends InstallAbstractModel
'blocksocial',
'wkfooterblock',
'wkhotelfilterblock',
+ 'wkhotelroom',
'wkabouthotelblock',
'wkhotelfeaturesblock',
- 'wkhotelroom',
'wktestimonialblock',
'bankwire',
'cheque',
diff --git a/mails/en/mails/en/booking_extra_services_text.tpl b/mails/en/booking_extra_services_text.tpl
similarity index 100%
rename from mails/en/mails/en/booking_extra_services_text.tpl
rename to mails/en/booking_extra_services_text.tpl
diff --git a/modules/dashproducts/dashproducts.php b/modules/dashproducts/dashproducts.php
index 70a8452d4..2b6cde382 100644
--- a/modules/dashproducts/dashproducts.php
+++ b/modules/dashproducts/dashproducts.php
@@ -255,7 +255,6 @@ class DashProducts extends Module
hbd.`id_product`,
hbd.`room_type_name` AS `product_name`,
COUNT(hbd.`id_room`) AS `total`,
- hbd.`total_price_tax_excl` AS `price`,
SUM(hbd.`total_price_tax_excl`) AS `sales`
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order`)
@@ -285,8 +284,6 @@ class DashProducts extends Module
$img = ImageManager::thumbnail($path_to_image, 'product_mini_'.$product_obj->id.'.'.$this->context->controller->imageType, 45, $this->context->controller->imageType);
}
- $productPrice = $product['price'];
-
$body[] = array(
array(
'id' => 'product',
@@ -297,8 +294,7 @@ class DashProducts extends Module
'id' => 'product',
'value' => ''.
- Tools::htmlentitiesUTF8($product['product_name']).' '.
- ' '.Tools::displayPrice($productPrice),
+ Tools::htmlentitiesUTF8($product['product_name']).'',
'class' => 'text-center'
),
array(
diff --git a/modules/hotelreservationsystem/classes/HotelAmenities.php b/modules/hotelreservationsystem/classes/HotelAmenities.php
index f517d891d..786c530b4 100644
--- a/modules/hotelreservationsystem/classes/HotelAmenities.php
+++ b/modules/hotelreservationsystem/classes/HotelAmenities.php
@@ -63,6 +63,8 @@ class HotelAmenities extends ObjectModel
if (file_exists($imgFile)) {
unlink($imgFile);
}
+ Db::getInstance()->delete('htl_branch_amenity', '`amenity_id` = '.(int)$id);
+ Db::getInstance()->delete('htl_room_type_amenity', '`amenity_id` = '.(int)$id);
return parent::delete();
}
return false;
diff --git a/modules/hotelreservationsystem/classes/HotelBookingDetail.php b/modules/hotelreservationsystem/classes/HotelBookingDetail.php
index da06f3240..b4ae5b8ea 100644
--- a/modules/hotelreservationsystem/classes/HotelBookingDetail.php
+++ b/modules/hotelreservationsystem/classes/HotelBookingDetail.php
@@ -1833,9 +1833,6 @@ class HotelBookingDetail extends ObjectModel
if (!$only_search_data) {
if (!empty($bookingData)) {
- $maxAmenities = (int) Configuration::get('WK_MAX_AMENITIES_ROOM_SEARCH');
- $maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_ROOM_SEARCH');
- $maxFeatureValues = (int) Configuration::get('WK_MAX_ROOM_TYPE_FEATURE_VALUES');
foreach ($bookingData['rm_data'] as $key => $value) {
// featured amenities for display in room type list
$featured_amenities = HotelRoomTypeAmenities::getAmenities(
@@ -1843,9 +1840,6 @@ class HotelBookingDetail extends ObjectModel
$context->language->id,
true
);
- if ($maxAmenities) {
- $featured_amenities = array_slice($featured_amenities, 0, $maxAmenities);
- }
if (!empty($amenities) && $amenities) {
$roomAmenityIds = array_column(HotelRoomTypeAmenities::getAmenities($value['id_product']), 'id');
if (array_diff($amenities, $roomAmenityIds)) {
@@ -1893,18 +1887,7 @@ class HotelBookingDetail extends ObjectModel
$featuredFeatures[$roomFeature['id_feature']]['values'][] = $roomFeature['value'];
}
}
- $featuredFeatures = array_values($featuredFeatures);
- if ($maxFeatures) {
- $featuredFeatures = array_slice($featuredFeatures, 0, $maxFeatures);
- }
- foreach ($featuredFeatures as &$featuredFeature) {
- if ($maxFeatureValues) {
- $featuredFeature['values'] = array_slice($featuredFeature['values'], 0, $maxFeatureValues);
- }
- $featuredFeature['value'] = implode(', ', $featuredFeature['values']);
- }
- unset($featuredFeature);
- $bookingData['rm_data'][$key]['featured_features'] = $featuredFeatures;
+ $bookingData['rm_data'][$key]['featured_features'] = array_values($featuredFeatures);
$bookingData['rm_data'][$key]['price'] = $prod_price;
$bookingData['rm_data'][$key]['feature_price'] = $productFeaturePrice;
$bookingData['rm_data'][$key]['price_without_reduction'] = $productPriceWithoutReduction;
diff --git a/modules/hotelreservationsystem/classes/HotelCartBookingData.php b/modules/hotelreservationsystem/classes/HotelCartBookingData.php
index 43b282ee2..de9156635 100644
--- a/modules/hotelreservationsystem/classes/HotelCartBookingData.php
+++ b/modules/hotelreservationsystem/classes/HotelCartBookingData.php
@@ -1515,7 +1515,7 @@ class HotelCartBookingData extends ObjectModel
$objCartBookingData = new HotelCartBookingData();
$objRoomTypeServiceProduct = new RoomTypeServiceProduct();
$objServiceProductCartDetail = new ServiceProductCartDetail();
- $maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_ROOM_SEARCH');
+ $maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_CHECKOUT');
$maxFeatureValues = (int) Configuration::get('WK_MAX_ROOM_TYPE_FEATURE_VALUES');
foreach ($cartRoomTypes as $prodKey => $product) {
diff --git a/modules/hotelreservationsystem/classes/HotelHelper.php b/modules/hotelreservationsystem/classes/HotelHelper.php
index b247b7dce..d8f9b10d2 100644
--- a/modules/hotelreservationsystem/classes/HotelHelper.php
+++ b/modules/hotelreservationsystem/classes/HotelHelper.php
@@ -1181,9 +1181,10 @@ class HotelHelper
Configuration::updateValue('PS_MIN_BOOKING_OFFSET', 0);
Configuration::updateValue('HTL_FEATURE_PRICING_PRIORITY', 'specific_date;special_day;date_range');
- Configuration::updateValue('WK_MAX_AMENITIES_ROOM_SEARCH', 0);
- Configuration::updateValue('WK_MAX_FEATURES_ROOM_SEARCH', 0);
- Configuration::updateValue('WK_MAX_ROOM_TYPE_FEATURE_VALUES', 0);
+ Configuration::updateValue('WK_MAX_AMENITIES_ROOM_SEARCH', 6);
+ Configuration::updateValue('WK_MAX_FEATURES_ROOM_SEARCH', 4);
+ Configuration::updateValue('WK_MAX_FEATURES_CHECKOUT', 0);
+ Configuration::updateValue('WK_MAX_ROOM_TYPE_FEATURE_VALUES', 2);
Configuration::updateValue('WK_GOOGLE_ACTIVE_MAP', 0);
Configuration::updateValue('WK_MAP_HOTEL_ACTIVE_ONLY', 1);
@@ -2297,9 +2298,52 @@ class HotelHelper
if (isset($roomTypeFeatures[$roomTypeDataDefult['name']])) {
$this->saveDummyRoomTypeFeatures($product, $roomTypeFeatures[$roomTypeDataDefult['name']]);
}
+
+ // assign amenities (with featured amenities) to the room type
+ $roomTypeAmenities = $this->getDefaultRoomTypeAmenities();
+ if (isset($roomTypeAmenities[$roomTypeDataDefult['name']])) {
+ $objRoomTypeAmenities = new HotelRoomTypeAmenities();
+ $objRoomTypeAmenities->saveRoomTypeAmenities(
+ $product_id,
+ $roomTypeAmenities[$roomTypeDataDefult['name']]['amenities'],
+ $roomTypeAmenities[$roomTypeDataDefult['name']]['featured']
+ );
+ }
}
}
+ /**
+ * Return the amenity ids (and the featured ones) of each demo room type, keyed by the room type name.
+ * Amenity ids are the ids of the default amenities created by insertHotelCommonAmenities().
+ *
+ * @return array
+ */
+ protected function getDefaultRoomTypeAmenities()
+ {
+ return array(
+ 'General Rooms' => array(
+ // Internet Access Free, Power Backup, Parking, NewsPaper In Lobby, Laundry Service, Gardens, Room Service, Welcome Drinks
+ 'amenities' => array(10, 25, 48, 12, 24, 55, 38, 14, 11),
+ 'featured' => array(10, 25, 48, 12, 24, 55, 38, 14),
+ ),
+ 'Delux Rooms' => array(
+ // + Bar / Lounge, Transfer Available
+ 'amenities' => array(10, 25, 48, 38, 14, 24, 55, 99, 12, 11),
+ 'featured' => array(10, 25, 38, 14, 48, 24, 55, 99),
+ ),
+ 'Executive Rooms' => array(
+ // + Health Club / Gym, Multi Cuisine Restaurant
+ 'amenities' => array(10, 25, 48, 38, 14, 24, 12, 69, 99, 100),
+ 'featured' => array(10, 38, 14, 69, 100, 48, 25, 24),
+ ),
+ 'Luxury Rooms' => array(
+ // + Car Rental, Swimming Pool, Jacuzzi, Spa Services
+ 'amenities' => array(10, 25, 48, 38, 14, 24, 40, 69, 93, 94, 87, 99, 100),
+ 'featured' => array(10, 38, 93, 94, 87, 100, 69, 99),
+ ),
+ );
+ }
+
/**
* Return the features (and their values) of each demo room type, keyed by the room type name.
*
diff --git a/modules/hotelreservationsystem/classes/HotelReservationSystemDb.php b/modules/hotelreservationsystem/classes/HotelReservationSystemDb.php
index 5b3126d7b..32542bc30 100644
--- a/modules/hotelreservationsystem/classes/HotelReservationSystemDb.php
+++ b/modules/hotelreservationsystem/classes/HotelReservationSystemDb.php
@@ -152,8 +152,7 @@ class HotelReservationSystemDb
`date_add` datetime NOT NULL,
`date_upd` datetime NOT NULL,
PRIMARY KEY (`id_branch_amenity`),
- CONSTRAINT `fk_htl_branch_amenity_amenity` FOREIGN KEY (`amenity_id`)
- REFERENCES `"._DB_PREFIX_."htl_amenity` (`id_amenity`) ON DELETE CASCADE
+ KEY `amenity_id` (`amenity_id`)
) ENGINE="._MYSQL_ENGINE_." DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;",
"CREATE TABLE IF NOT EXISTS `"._DB_PREFIX_."htl_room_type_amenity` (
@@ -165,8 +164,7 @@ class HotelReservationSystemDb
`date_upd` datetime NOT NULL,
PRIMARY KEY (`id_room_type_amenity`),
KEY `id_product` (`id_product`),
- CONSTRAINT `fk_htl_room_type_amenity_amenity` FOREIGN KEY (`amenity_id`)
- REFERENCES `"._DB_PREFIX_."htl_amenity` (`id_amenity`) ON DELETE CASCADE
+ KEY `amenity_id` (`amenity_id`)
) ENGINE="._MYSQL_ENGINE_." DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;",
"CREATE TABLE IF NOT EXISTS `"._DB_PREFIX_."htl_cart_booking_data` (
diff --git a/modules/hotelreservationsystem/controllers/admin/AdminHotelGeneralSettingsController.php b/modules/hotelreservationsystem/controllers/admin/AdminHotelGeneralSettingsController.php
index f978ca93e..f00149fbe 100644
--- a/modules/hotelreservationsystem/controllers/admin/AdminHotelGeneralSettingsController.php
+++ b/modules/hotelreservationsystem/controllers/admin/AdminHotelGeneralSettingsController.php
@@ -166,7 +166,7 @@ class AdminHotelGeneralSettingsController extends ModuleAdminController
'hint' => $this->l('Enable to display hotels list on the contact us page.'),
),
'WK_MAX_AMENITIES_ROOM_SEARCH' => array(
- 'title' => $this->l('Maximum featured amenities in room search'),
+ 'title' => $this->l('Max featured amenities in room search'),
'hint' => $this->l('Maximum number of featured amenities to display for each room type on the front office room search page.'),
'desc' => $this->l('Set to 0 to display all featured amenities.'),
'type' => 'text',
@@ -175,7 +175,7 @@ class AdminHotelGeneralSettingsController extends ModuleAdminController
'default' => '0',
),
'WK_MAX_FEATURES_ROOM_SEARCH' => array(
- 'title' => $this->l('Maximum featured features in room search'),
+ 'title' => $this->l('Max Featured room type features on room search page'),
'hint' => $this->l('Maximum number of featured features to display for each room type on the front office room search page.'),
'desc' => $this->l('Set to 0 to display all featured features.'),
'type' => 'text',
@@ -183,9 +183,18 @@ class AdminHotelGeneralSettingsController extends ModuleAdminController
'class' => 'fixed-width-xl',
'default' => '0',
),
+ 'WK_MAX_FEATURES_CHECKOUT' => array(
+ 'title' => $this->l('Max Featured room type features on Checkout page'),
+ 'hint' => $this->l('Maximum number of featured features to display for each room type on the front office checkout page.'),
+ 'desc' => $this->l('Set to 0 to display all featured features.'),
+ 'type' => 'text',
+ 'cast' => 'intval',
+ 'class' => 'fixed-width-xl',
+ 'default' => '0',
+ ),
'WK_MAX_ROOM_TYPE_FEATURE_VALUES' => array(
- 'title' => $this->l('Maximum room type feature values per featured feature'),
- 'hint' => $this->l('Maximum number of room type feature values to display for each featured feature on the front office room search and order pages. For example, if the featured feature Bed Type has the values Single, Double and Queen and this is set to 2, only "Single, Double" is displayed.'),
+ 'title' => $this->l('Max display values for room type Featured features'),
+ 'hint' => $this->l('Maximum number of room type feature values to display for each featured feature in room search results and orders. For example, if the featured feature Bed Type has the values Single, Double, and Queen, and this setting is set to 2, only Single, Double will be displayed.'),
'desc' => $this->l('Set to 0 to display all values of each featured feature.'),
'type' => 'text',
'cast' => 'intval',
@@ -562,6 +571,9 @@ class AdminHotelGeneralSettingsController extends ModuleAdminController
if (!Validate::isUnsignedInt(Tools::getValue('WK_MAX_FEATURES_ROOM_SEARCH'))) {
$this->errors[] = $this->l('Maximum featured features in room search is invalid. Enter 0 or a positive number.');
}
+ if (!Validate::isUnsignedInt(Tools::getValue('WK_MAX_FEATURES_CHECKOUT'))) {
+ $this->errors[] = $this->l('Maximum featured features in checkout is invalid. Enter 0 or a positive number.');
+ }
if (!Validate::isUnsignedInt(Tools::getValue('WK_MAX_ROOM_TYPE_FEATURE_VALUES'))) {
$this->errors[] = $this->l('Maximum room type feature values per featured feature is invalid. Enter 0 or a positive number.');
}
diff --git a/modules/hotelreservationsystem/hotelreservationsystem.php b/modules/hotelreservationsystem/hotelreservationsystem.php
index 94b683e54..9cb776db0 100644
--- a/modules/hotelreservationsystem/hotelreservationsystem.php
+++ b/modules/hotelreservationsystem/hotelreservationsystem.php
@@ -610,6 +610,7 @@ class HotelReservationSystem extends Module
'WK_DISPLAY_CONTACT_PAGE_HOTEL_LIST',
'WK_MAX_AMENITIES_ROOM_SEARCH',
'WK_MAX_FEATURES_ROOM_SEARCH',
+ 'WK_MAX_FEATURES_CHECKOUT',
'WK_MAX_ROOM_TYPE_FEATURE_VALUES',
'QLO_HEADER_MEDIA_TYPE',
'QLO_HEADER_CONTENT_ALIGN',
diff --git a/modules/qlocleaner/qlocleaner.php b/modules/qlocleaner/qlocleaner.php
index 2cb11959f..011615f49 100644
--- a/modules/qlocleaner/qlocleaner.php
+++ b/modules/qlocleaner/qlocleaner.php
@@ -686,8 +686,8 @@ class QloCleaner extends Module
array('htl_testimonials_block_data_lang', 'id_testimonial_block', 'htl_testimonials_block_data', 'id_testimonial_block'),
array('htl_branch_info_lang', 'id_lang', 'lang', 'id_lang'),
array('htl_branch_info_lang', 'id', 'htl_branch_info', 'id'),
- array('htl_features_lang', 'id_lang', 'lang', 'id_lang'),
- array('htl_features_lang', 'id', 'htl_features', 'id'),
+ array('htl_amenity_lang', 'id_lang', 'lang', 'id_lang'),
+ array('htl_amenity_lang', 'id_amenity', 'htl_amenity', 'id_amenity'),
array('htl_room_type_feature_pricing', 'id_product', 'product', 'id_product'),
array('htl_room_type_feature_pricing_restriction', 'id_feature_price', 'htl_room_type_feature_pricing', 'id_feature_price'),
array('htl_room_type_feature_pricing_group', 'id_group', 'group', 'id_group'),
diff --git a/modules/qlogooglehotelconnector/controllers/admin/AdminGoogleHotelLogsController.php b/modules/qlogooglehotelconnector/controllers/admin/AdminGoogleHotelLogsController.php
index f2e44715e..7015a13b5 100644
--- a/modules/qlogooglehotelconnector/controllers/admin/AdminGoogleHotelLogsController.php
+++ b/modules/qlogooglehotelconnector/controllers/admin/AdminGoogleHotelLogsController.php
@@ -36,12 +36,12 @@ class AdminGoogleHotelLogsController extends ModuleAdminController
// Every QghcApiLog::TYPE_* must have an entry here, or it shows as its raw
// value in the filter dropdown, the list column, and the detail view.
$this->typeLabels = array(
- QghcApiLog::TYPE_ARI_FULL => 'Full Availability & Rates Update',
- QghcApiLog::TYPE_ARI_CHANGE => 'Availability & Rates Change Update',
- QghcApiLog::TYPE_GOOGLE_STATUS => 'Google Status',
- QghcApiLog::TYPE_CONNECTION => 'Connection',
- QghcApiLog::TYPE_DELETE_CONNECTION => 'Delete Connection',
- QghcApiLog::TYPE_HOTEL_LIST_FEED => 'Property Detail',
+ QghcApiLog::TYPE_ARI_FULL => $this->l('Full Availability & Rates Update'),
+ QghcApiLog::TYPE_ARI_CHANGE => $this->l('Availability & Rates Change Update'),
+ QghcApiLog::TYPE_GOOGLE_STATUS => $this->l('Google Status'),
+ QghcApiLog::TYPE_CONNECTION => $this->l('Connection'),
+ QghcApiLog::TYPE_DELETE_CONNECTION => $this->l('Delete Connection'),
+ QghcApiLog::TYPE_HOTEL_LIST_FEED => $this->l('Properties Details'),
);
$this->bootstrap = true;
diff --git a/modules/qlogooglehotelconnector/qlogooglehotelconnector.php b/modules/qlogooglehotelconnector/qlogooglehotelconnector.php
index 47296fe35..10f66c56f 100644
--- a/modules/qlogooglehotelconnector/qlogooglehotelconnector.php
+++ b/modules/qlogooglehotelconnector/qlogooglehotelconnector.php
@@ -903,7 +903,7 @@ class QloGoogleHotelConnector extends Module
$oldStatus = (int)$oldRoom->id_status;
$newStatus = isset($newRoomInfo['id_status']) ? (int)$newRoomInfo['id_status'] : $oldStatus;
- if (($newStatus !== HotelRoomInformation::STATUS_TEMPORARY_INACTIVE) && ($oldStatus !== $newStatus)) {
+ if ($oldStatus !== $newStatus) {
$objHotelRoomInfo = new HotelRoomInformation();
$roomInfoArray = $objHotelRoomInfo->getHotelRoomInfoByProductId((int)$oldRoom->id_product);
if (empty($roomInfoArray)) {
diff --git a/modules/wkabouthotelblock/views/css/WkAboutHotelBlockFront.css b/modules/wkabouthotelblock/views/css/WkAboutHotelBlockFront.css
index bfe15adaa..332b27df1 100644
--- a/modules/wkabouthotelblock/views/css/WkAboutHotelBlockFront.css
+++ b/modules/wkabouthotelblock/views/css/WkAboutHotelBlockFront.css
@@ -20,8 +20,11 @@
* @license https://opensource.org/license/osl-3-0-php Open Software License version 3.0
*/
-.htlInterior-owlCarousel .owl-stage-outer {
- border: 1px solid #ccc;
+@media (max-width: 767px) {
+ .htlInterior-owlCarousel .owl-carousel {
+ width: 100%;
+ margin: 0;
+ }
}
.interiorImgWrapper {
width: 100%;
@@ -46,32 +49,24 @@
height: 100%;
object-fit: cover;
}
-.htlInterior-owlCarousel .owl-prev, .htlInterior-owlCarousel .owl-next{
- font-size: 40px;
- font-weight: 200;
- position: absolute;
- top: 0;
- bottom: 0;
- z-index: 10;
-}
-.htlInterior-owlCarousel .owl-prev {
- left: 0;
-}
-.htlInterior-owlCarousel .owl-next {
- right: 0;
-}
.nav-direction-wrapper{
- display: table;
- height: 100%;
+ display: block;
}
.nav-direction-primary {
- display: table-cell;
- vertical-align: middle;
+ display: block;
}
.nav-direction-secondary {
- padding: 40px 18px;
- opacity: 0.85;
- background-color: #ffffff;
+ background: rgba(0, 0, 0, 0.4);
+ color: #fff;
+ border-radius: 2px;
+ padding: 8px 14px;
+ font-size: 20px;
+ line-height: 1;
+ transition: background 0.2s;
+}
+.htlInterior-owlCarousel .owl-prev:hover .nav-direction-secondary,
+.htlInterior-owlCarousel .owl-next:hover .nav-direction-secondary {
+ background: rgba(0, 0, 0, 0.65);
}
.interiorHoverBlockWrapper {
position: absolute;
@@ -79,7 +74,7 @@
right: 0;
top: 0;
bottom: 0;
- background-image: url("../img/black-opacity-5.png");
+ background-color: rgba(0, 0, 0, 0.5);
cursor: pointer;
display: none;
}
@@ -94,4 +89,88 @@
font-size: 50px;
text-align: center;
vertical-align: middle;
-}
\ No newline at end of file
+}
+
+/* gallery: pages of 8 square photos (4 x 2) with dots below */
+.htlInterior-owlCarousel .owl-stage-outer {
+ border: 0;
+}
+.interiorImgWrapper {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 8px;
+ height: auto;
+}
+.interiorbox {
+ padding-bottom: 100%;
+}
+.interiorboxInner {
+ border: 0;
+ border-radius: 3px;
+}
+.htlInterior-owlCarousel .owl-dots {
+ display: flex;
+ justify-content: center;
+ gap: 6px;
+ margin-top: 24px;
+}
+.htlInterior-owlCarousel .owl-dots .owl-dot span {
+ display: block;
+ width: 7px;
+ height: 7px;
+ margin: 0;
+ border-radius: 50%;
+ background: #C4C4C4;
+}
+.htlInterior-owlCarousel .owl-dots .owl-dot.active span {
+ background: #333;
+}
+/* touch screens have no hover, so always show the zoom badge */
+@media (hover: none) {
+ #hotelInteriorBlock .interiorbox::after {
+ content: "\f00e";
+ font-family: "FontAwesome";
+ position: absolute;
+ right: 8px;
+ top: 8px;
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: rgba(0, 0, 0, .45);
+ color: #fff;
+ font-size: 14px;
+ line-height: 28px;
+ text-align: center;
+ pointer-events: none;
+ }
+}
+
+/* gallery lightbox: square photo, dark overlay, thumbnail strip with round prev/next at the bottom */
+html body .htl-gallery-fancybox .fancybox-skin, html body .htl-light-fancybox .fancybox-skin { border-radius: 5px; }
+html body .htl-gallery-fancybox .fancybox-skin { padding: 0 !important; background: transparent; border-radius: 5px; box-shadow: none; }
+html body .htl-gallery-fancybox .fancybox-outer, html body .htl-gallery-fancybox .fancybox-inner { border-radius: 5px; overflow: hidden; }
+html body .htl-gallery-fancybox .fancybox-image { border-radius: 5px; }
+html body .htl-light-fancybox .fancybox-title-outside-wrap { position: static; display: table; width: auto; margin: 14px auto 0; padding: 6px 20px; border-radius: 16px; background: #2b2b2b; font-size: 13px; font-weight: 700; line-height: 1.4; color: #fff; text-align: center; }
+html body .htl-gallery-fancybox .fancybox-title-outside-wrap { position: static; display: table; width: auto; margin: 14px auto 0; padding: 6px 20px; border: 1px solid rgba(255, 255, 255, .35); border-radius: 16px; background: #111; font-size: 13px; font-weight: 700; line-height: 1.4; color: #fff; text-align: center; }
+.htl-gal-thumbs { position: fixed; left: 0; right: 0; bottom: 49px; z-index: 8040; display: flex; align-items: center; gap: 16px; padding: 0 28px; }
+.htl-gal-thumbs__track { flex: 1 1 auto; display: flex; gap: 12px; overflow-x: auto; scroll-behavior: smooth; scrollbar-width: none; padding: 2px 0; }
+.htl-gal-thumbs__track::-webkit-scrollbar { display: none; }
+.htl-gal-thumbs__item { flex: 0 0 auto; width: 96px; height: 96px; padding: 0; border: 2px solid transparent; border-radius: 2px; background: #000; overflow: hidden; opacity: .6; cursor: pointer; transition: opacity .2s ease, border-color .2s ease; }
+.htl-gal-thumbs__item img { display: block; width: 100%; height: 100%; object-fit: cover; }
+.htl-gal-thumbs__item:hover, .htl-gal-thumbs__item.is-active { opacity: 1; }
+.htl-gal-thumbs__item.is-active { border-color: #fff; }
+.htl-gal-thumbs__nav { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; padding: 8px 15px; border: 0; border-radius: 50%; background: #fff; color: #333; font-size: 22px; line-height: 1; box-shadow: 0 2px 10px rgba(0, 0, 0, .3); cursor: pointer; transition: transform .2s ease; }
+.htl-gal-thumbs__nav i { display: block; line-height: 1; }
+@media (max-width: 767px) {
+ .htl-gal-thumbs { bottom: 20px; padding: 0 12px; gap: 8px; }
+ .htl-gal-thumbs__item { width: 60px; height: 60px; }
+ .htl-gal-thumbs__nav { width: 36px; height: 36px; font-size: 20px; }
+ html body .htl-gallery-fancybox .fancybox-close { top: 8px; right: 8px; }
+}
+html body .fancybox-overlay.htl-gallery-overlay { opacity: 1 !important; background: rgba(43, 43, 43, 0.95) !important; }
+
+/* the theme's a:focus outline would cover half of the photo after a click on next/prev, keep it for keyboard users only */
+html body .htl-light-fancybox .fancybox-nav:focus:not(:focus-visible),
+html body .htl-gallery-fancybox .fancybox-nav:focus:not(:focus-visible) {
+ outline: 0;
+}
diff --git a/modules/wkabouthotelblock/views/js/WkAboutHotelBlockFront.js b/modules/wkabouthotelblock/views/js/WkAboutHotelBlockFront.js
index 331cd5051..40c2a1af8 100644
--- a/modules/wkabouthotelblock/views/js/WkAboutHotelBlockFront.js
+++ b/modules/wkabouthotelblock/views/js/WkAboutHotelBlockFront.js
@@ -23,26 +23,19 @@
$(document).ready(function(){
$('.htlInterior-owlCarousel .owl-carousel').owlCarousel({
loop:true,
- nav:true,
- navSpeed:1000,
- navText:['',''],
- dots:false,
+ nav:false,
+ dots:true,
items: 1,
+ margin: 8,
+ slideBy: 'page',
+ responsive: {576: {items: 2}},
autoHeight:true,
autoplay:true,
autoplaySpeed:1000,
autoplayTimeout:5000,
autoplayHoverPause:true,
responsiveClass:true,
- rtl: language_is_rtl,
- responsive:{
- 1200:{
- items:3
- },
- 480:{
- items:2
- },
- }
+ rtl: language_is_rtl
});
$('.interiorbox').on('mouseenter', function(){
@@ -52,20 +45,123 @@ $(document).ready(function(){
$(this).find('div.interiorHoverBlockWrapper').hide();
});
- $("div.interiorbox").fancybox({
- autoDimensions: true,
- autoScale: true,
- autoSize: true,
- centerOnScroll: true,
- height: 'auto',
- scrolling: 'auto',
- width: 'auto',
- maxWidth: 700,
- 'hideOnContentClick': false,
- helpers: {
- overlay: {
- locked: false
- }
- }
- });
-});
\ No newline at end of file
+ // gallery lightbox: fancybox with a thumbnail strip and round prev/next buttons at the bottom
+ var $thumbs = null;
+
+ function buildThumbs() {
+ var group = $.fancybox.group || [];
+ if (group.length < 2) {
+ return;
+ }
+ $thumbs = $('');
+ var $track = $thumbs.find('.htl-gal-thumbs__track');
+ $.each(group, function(i, item) {
+ $(' ')
+ .attr('aria-label', (i + 1) + ' / ' + group.length)
+ .append($(' ').attr('src', item.href))
+ .on('click', function() { this.blur(); $.fancybox.jumpto(i); })
+ .appendTo($track);
+ });
+ $thumbs.find('.htl-gal-thumbs__prev').on('click', function() { this.blur(); $.fancybox.prev(); });
+ $thumbs.find('.htl-gal-thumbs__next').on('click', function() { this.blur(); $.fancybox.next(); });
+ $thumbs.appendTo('body');
+ }
+
+ function markActiveThumb() {
+ if (!$thumbs) {
+ return;
+ }
+ var $items = $thumbs.find('.htl-gal-thumbs__item').removeClass('is-active');
+ var $active = $items.eq($.fancybox.current.index).addClass('is-active');
+ var track = $thumbs.find('.htl-gal-thumbs__track').get(0);
+ if ($active.length && track) {
+ track.scrollLeft = $active.get(0).offsetLeft - (track.clientWidth - $active.outerWidth()) / 2;
+ }
+ }
+
+ // options of the dark gallery with a thumbnail strip
+ var thumbsGalleryOptions = {
+ padding: 0,
+ margin: [48, 48, 190, 48],
+ wrapCSS: 'htl-gallery-fancybox',
+ arrows: false,
+ helpers: {overlay: {locked: false, css: {background: 'rgba(43, 43, 43, 0.9)'}}},
+ beforeLoad: function() {
+ var compact = window.innerWidth < 768 || window.innerHeight < 500;
+ this.margin = window.innerHeight < 500 ? [36, 12, 84, 12] : (compact ? [48, 12, 110, 12] : [48, 48, 190, 48]);
+ },
+ afterLoad: function() {
+ // photos are shown as large as the screen allows (up to 589px), not at their natural size
+ var smallW = window.innerWidth < 768;
+ var shortH = window.innerHeight < 500;
+ var maxH = Math.min(window.innerHeight - (shortH ? 120 : (smallW ? 158 : 238)), 589);
+ var maxW = window.innerWidth - (shortH || smallW ? 24 : 96);
+ var ratio = this.width / this.height;
+ var height = maxH;
+ var width = height * ratio;
+ if (width > maxW) {
+ width = maxW;
+ height = width / ratio;
+ }
+ this.width = Math.round(width);
+ this.height = Math.round(height);
+ },
+ beforeShow: function() {
+ $('.fancybox-overlay').addClass('htl-gallery-overlay');
+ if (!$thumbs) {
+ buildThumbs();
+ }
+ },
+ afterShow: markActiveThumb,
+ afterClose: function() {
+ if ($thumbs) {
+ $thumbs.remove();
+ $thumbs = null;
+ }
+ }
+ };
+
+ // options of the light gallery with the next and previous arrows on the photo
+ var lightGalleryOptions = {
+ wrapCSS: 'htl-light-fancybox',
+ maxWidth: 700,
+ nextEffect: 'fade',
+ prevEffect: 'fade',
+ helpers: {overlay: {locked: false}}
+ };
+
+ /**
+ * Opens the photos of a slider in a lightbox when one is clicked.
+ * Usage: $('.owl-carousel').interiorBoxGallery({thumbs: true});
+ * thumbs true: dark gallery with a thumbnail strip, false: light gallery with arrows.
+ */
+ $.fn.interiorBoxGallery = function(options) {
+ var settings = $.extend({thumbs: true, itemSelector: '.interiorbox[href]'}, options);
+
+ return this.each(function() {
+ var $slider = $(this);
+ // photos are listed once, the slider also holds cloned copies when it loops
+ $slider.on('click', settings.itemSelector, function(e) {
+ e.preventDefault();
+ var items = [];
+ var index = 0;
+ var clicked = $(e.currentTarget).attr('href');
+ $slider.find('.owl-item:not(.cloned) ' + settings.itemSelector).not('.interiorbox-filler').each(function(i) {
+ items.push({href: $(this).attr('href'), title: $(this).attr('title') || ''});
+ if ($(this).attr('href') === clicked) {
+ index = i;
+ }
+ });
+ $.fancybox.open(items, $.extend(true, {
+ index: index,
+ type: 'image',
+ openEffect: 'fade',
+ closeEffect: 'fade',
+ helpers: {title: {type: 'outside'}}
+ }, settings.thumbs ? thumbsGalleryOptions : lightGalleryOptions));
+ });
+ });
+ };
+
+ $('.htlInterior-owlCarousel .owl-carousel').interiorBoxGallery({thumbs: true});
+});
diff --git a/modules/wkabouthotelblock/views/templates/hook/hotelInteriorBlock.tpl b/modules/wkabouthotelblock/views/templates/hook/hotelInteriorBlock.tpl
index 71ae109f1..646d5b7f8 100644
--- a/modules/wkabouthotelblock/views/templates/hook/hotelInteriorBlock.tpl
+++ b/modules/wkabouthotelblock/views/templates/hook/hotelInteriorBlock.tpl
@@ -44,12 +44,11 @@
- {assign var='intImgIteration' value=0}
{foreach from=$InteriorImg item=img_name name=intImg}
- {if $smarty.foreach.intImg.iteration%3 == 1}
+ {if $smarty.foreach.intImg.iteration%4 == 1}
- {if $smarty.foreach.intImg.iteration%3 == 0}
+ {if $smarty.foreach.intImg.iteration%4 == 0 || $smarty.foreach.intImg.last}
{/if}
- {assign var='intImgIteration' value=$smarty.foreach.intImg.iteration}
{/foreach}
- {if $intImgIteration%3}
- {assign var='intImgLeft' value=3-($intImgIteration%3)}
- {for $foo=1 to $intImgLeft}
-
-
-
-
-
- {/for}
-
- {/if}
diff --git a/modules/wkabouthotelblock/wkabouthotelblock.php b/modules/wkabouthotelblock/wkabouthotelblock.php
index 0a6faa5f0..3fbdb98e9 100644
--- a/modules/wkabouthotelblock/wkabouthotelblock.php
+++ b/modules/wkabouthotelblock/wkabouthotelblock.php
@@ -47,6 +47,7 @@ class WkAboutHotelBlock extends Module
public function hookDisplayHome()
{
+ $this->context->controller->addJqueryPlugin('fancybox');
$this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.carousel.min.css');
$this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.theme.default.min.css');
$this->context->controller->addJS(_PS_JS_DIR_.'owl-carousel/owl.carousel.min.js');
@@ -58,6 +59,13 @@ class WkAboutHotelBlock extends Module
$objHtlInteriorImg = new WkHotelInteriorImage();
$InteriorImg = $objHtlInteriorImg->getHotelInteriorImg(1);
+ // every slide shows 4 photos, so the last slide is completed with photos from the start
+ if ($InteriorImg && ($missing = (4 - count($InteriorImg) % 4) % 4)) {
+ foreach (array_slice($InteriorImg, 0, $missing) as $filler) {
+ $filler['is_filler'] = 1;
+ $InteriorImg[] = $filler;
+ }
+ }
$this->context->smarty->assign(
array(
diff --git a/modules/wkhotelfeaturesblock/views/css/wkHotelFeaturesBlockFront.css b/modules/wkhotelfeaturesblock/views/css/wkHotelFeaturesBlockFront.css
index 7e80c83b4..05cafee55 100644
--- a/modules/wkhotelfeaturesblock/views/css/wkHotelFeaturesBlockFront.css
+++ b/modules/wkhotelfeaturesblock/views/css/wkHotelFeaturesBlockFront.css
@@ -62,4 +62,12 @@
.amenity_desc_hr {
border-top: 1px solid #333333;
width: 60px;
+}
+@media (max-width: 767px) {
+ #hotelAmenitiesBlock .visible-xs .amenity_img_secondary {
+ margin: 0 15px;
+ }
+ #hotelAmenitiesBlock .visible-xs .amenity_desc_secondary {
+ padding: 30px 15px;
+ }
}
\ No newline at end of file
diff --git a/modules/wkhotelfeaturesblock/views/js/wkHotelFeaturesBlockFront.js b/modules/wkhotelfeaturesblock/views/js/wkHotelFeaturesBlockFront.js
index 6be596818..6557ea3d7 100644
--- a/modules/wkhotelfeaturesblock/views/js/wkHotelFeaturesBlockFront.js
+++ b/modules/wkhotelfeaturesblock/views/js/wkHotelFeaturesBlockFront.js
@@ -37,6 +37,12 @@ function setAmenitiesWrapperHeight() {
function setAmenitiesDescContHeight() {
$('.amenity_desc_cont').each(function () {
+ // on mobile the image and text stack instead of sitting side by side,
+ // so matching the text box's height to the combined row height leaves a large blank gap
+ if ($(this).closest('.visible-xs').length) {
+ $(this).css('height', 'auto');
+ return;
+ }
var amenityDescParentHeight = $(this).parent('div.amenity_content').height();
$(this).css('height', amenityDescParentHeight);
});
diff --git a/modules/wkhotelfilterblock/views/css/wkhotelfilterblock.css b/modules/wkhotelfilterblock/views/css/wkhotelfilterblock.css
index 5c2e5cdbd..292ca4cca 100644
--- a/modules/wkhotelfilterblock/views/css/wkhotelfilterblock.css
+++ b/modules/wkhotelfilterblock/views/css/wkhotelfilterblock.css
@@ -58,7 +58,7 @@
.layered_filt
{
- margin-bottom: 5px;
+ margin-bottom: 8px;
}
.layered_filt_category
@@ -111,4 +111,11 @@
#filter_price_silder.ui-widget-content {
border: none;
-}
\ No newline at end of file
+}
+
+.layered_filt_label {
+ display: block;
+ margin: 0;
+ font-weight: normal;
+ cursor: pointer;
+}
diff --git a/modules/wkhotelfilterblock/views/js/wkhotelfilterblock.js b/modules/wkhotelfilterblock/views/js/wkhotelfilterblock.js
index bfa546c65..4c1dd5f6b 100644
--- a/modules/wkhotelfilterblock/views/js/wkhotelfilterblock.js
+++ b/modules/wkhotelfilterblock/views/js/wkhotelfilterblock.js
@@ -132,6 +132,7 @@ $(document).ready(function()
triggerFilter(1, 0, 0, 0);
$('.filter').on('click', function()
{
+ $(this).parent().toggleClass('checked', this.checked);
triggerFilter();
});
@@ -202,4 +203,47 @@ $(document).ready(function()
});
return 1;
}
+ var $conts = $('#filter_results > .col-sm-12 > .layered_filter_cont');
+ if ($conts.length) {
+ var $nav = $('
');
+ $conts.each(function () {
+ $nav.append($(' ').text($(this).find('.lf_headingmain_wrapper span').first().text()).append(' '));
+ });
+ $('#filter_results > .col-sm-12').prepend($nav);
+
+ var $sheet = $('');
+ $('.sort_result').each(function () {
+ $sheet.find('ul').append($(' ').text($(this).text()).data('source', $(this)));
+ });
+ var $bar = $(' ' + mfSortTxt + ' ' + mfFilterTxt + '
');
+ $('body').append($sheet, $bar);
+
+ function selectTab(i) {
+ $nav.children().removeClass('mf_active').eq(i).addClass('mf_active');
+ $conts.removeClass('mf_active').eq(i).addClass('mf_active');
+ }
+ selectTab(0);
+
+ function updateCounts() {
+ var total = 0;
+ $conts.each(function (i) {
+ var n = $(this).find('input.filter:checked').length;
+ var v = $(this).find('#filter_price_silder').length ? $('#filter_price_silder').slider('values') : 0;
+ n += v && (v[0] != min_price || v[1] != max_price) ? 1 : 0;
+ total += n;
+ $nav.children().eq(i).children('b').text(n || '');
+ });
+ $bar.find('.mf_filter b').text(total || '');
+ }
+ $(document).on('click change slidechange', '#filter_results', function () { setTimeout(updateCounts, 50); });
+
+ $nav.on('click', '.mf_tab', function () { selectTab($(this).index()); });
+ $bar.find('.mf_filter').on('click', function () { $('body').addClass('mf_filter_open'); });
+ $bar.find('.mf_sort').on('click', function () { $sheet.addClass('mf_open'); });
+ $('.mf_back, .mf_apply').on('click', function () { $('body').removeClass('mf_filter_open'); });
+ $('.mf_clear_all').on('click', function () { $('.clear_filter').trigger('click'); });
+ $sheet.on('click', function (e) { if (e.target === this || $(e.target).is('button')) $sheet.removeClass('mf_open'); });
+ $sheet.on('click', 'li', function () { $(this).addClass('mf_selected').siblings().removeClass('mf_selected').end().data('source').trigger('click'); $sheet.removeClass('mf_open'); });
+ $sheet.find('li').first().addClass('mf_selected');
+ }
});
\ No newline at end of file
diff --git a/modules/wkhotelfilterblock/views/templates/hook/htlfilterblock.tpl b/modules/wkhotelfilterblock/views/templates/hook/htlfilterblock.tpl
index 0fab3ecf3..007862b39 100644
--- a/modules/wkhotelfilterblock/views/templates/hook/htlfilterblock.tpl
+++ b/modules/wkhotelfilterblock/views/templates/hook/htlfilterblock.tpl
@@ -22,6 +22,12 @@
{block name='room_types_filters_block'}
+ {* mobile only: header of the full screen filters panel *}
+
{block name='room_types_price_filter'}
{if isset($config) && $config['SHOW_PRICE_FILTER']}
@@ -61,8 +67,10 @@
{foreach $all_amenities as $amenity}
{/foreach}
@@ -86,8 +94,10 @@
{foreach $feature.values as $featureValue}
{/foreach}
@@ -96,6 +106,9 @@
{/if}
{/block}
+ {block name='room_types_filters_mobile_apply'}
+
+ {/block}
{block name='room_types_filters_js_vars'}
{strip}
@@ -108,6 +121,9 @@
{addJsDef max_price = $max_price}
{addJsDef warning_num = $warning_num}
+ {addJsDefL name=mfSortTxt}{l s='Sort' js=1 mod='wkhotelfilterblock'}{/addJsDefL}
+ {addJsDefL name=mfSortByTxt}{l s='Sort by' js=1 mod='wkhotelfilterblock'}{/addJsDefL}
+ {addJsDefL name=mfFilterTxt}{l s='Filter' js=1 mod='wkhotelfilterblock'}{/addJsDefL}
{addJsDefL name=viewMoreTxt}{l s='View More' js=1 mod='wkhotelfilterblock'}{/addJsDefL}
{addJsDefL name=bookNowTxt}{l s='Book Now' js=1 mod='wkhotelfilterblock'}{/addJsDefL}
{/strip}
diff --git a/modules/wkhotelroom/views/css/WkHotelRoomBlockFront.css b/modules/wkhotelroom/views/css/WkHotelRoomBlockFront.css
index d3776d0e5..3a8228f5d 100644
--- a/modules/wkhotelroom/views/css/WkHotelRoomBlockFront.css
+++ b/modules/wkhotelroom/views/css/WkHotelRoomBlockFront.css
@@ -50,17 +50,24 @@
float :right;
}
}
+@media (min-width: 768px) and (max-width: 991px) {
+ #hotelRoomsBlock .col-md-6 {
+ width: 50%;
+ float: left;
+ }
+}
@media (max-width: 530px) {
+ #hotelRoomsBlock .col-md-6 > a img.width-100 {
+ width: 100%;
+ margin: 0;
+ }
.hotelRoomDescContainer{
- padding: 10px;
+ padding: 15px 0px;
}
.wk_roomType_price_type{
font-size: 16px;
font-weight: 400;
}
- .htlRoomTypeNameText {
- width: 100%;
- }
.room_type_old_price {
width: 100%;
@@ -91,12 +98,231 @@ a.htlRoomTypeBookNow {
text-transform: capitalize;
}
-.btn.htlRoomTypeBookNow:hover{
- border: 1px solid #333333;
-}
.room_type_old_price {
text-decoration: line-through;
color:#555555;
font-size:14px;
-}
\ No newline at end of file
+}
+
+/* room type cards slider */
+.htlRoom-owlCarousel .owl-carousel {
+ padding: 0 0 6px;
+}
+.htlRoomCard {
+ background: #fff;
+ border: 1px solid #E5E5E5;
+ border-radius: 4px;
+ overflow: hidden;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, .06);
+}
+.htlRoomCardImg {
+ display: block;
+ aspect-ratio: 4 / 3;
+ overflow: hidden;
+}
+.htlRoomCardImg img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+.htlRoomCardBody {
+ padding: 16px;
+}
+.htlRoomCardBody .htlRoomTypeNameText {
+ float: none;
+ margin: 0 0 12px;
+ font-size: 22px;
+}
+.htlRoomCardFeatured {
+ padding: 12px 0;
+ overflow: hidden;
+ white-space: nowrap;
+ color: #555;
+ font-size: 14px;
+}
+.htlRoomCardFeatured img.htlRoomFeatureImg {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ max-width: 16px;
+ margin-right: 4px;
+ object-fit: contain;
+ vertical-align: -3px;
+}
+.htlRoomCardAmen > span img.htlRoomAmenityImg {
+ width: 18px;
+ height: 18px;
+ max-width: 18px;
+ object-fit: contain;
+}
+/* one line for all features. Each feature, with the | in front of it, is one block, so when the row is wider
+ than the card the whole feature is dropped and the ellipsis never follows a lonely | */
+.htlRoomCardFeatured > span {
+ display: inline-block;
+ vertical-align: top;
+}
+.htlRoomCardFeatured > span + span:not(.htlRoomFeatureMore):before {
+ content: "|";
+ margin: 0 5px;
+ color: #BBB;
+}
+.htlRoomCardFeatured i {
+ margin-right: 4px;
+ color: #777;
+}
+.htlRoomCardMeta {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ padding: 12px 0;
+ border-top: 1px solid #E5E5E5;
+ color: #555;
+ font-size: 15px;
+}
+.htlRoomCardGuests i {
+ margin-right: 4px;
+ color: #777;
+}
+.htlRoomCardAmen {
+ display: flex;
+ flex: 1 1 auto;
+ gap: 6px;
+ min-width: 0;
+ overflow: hidden;
+}
+.htlRoomCardAmen > span.htlRoomAmenMore {
+ width: auto;
+ min-width: 32px;
+ padding: 0 8px;
+ border-radius: 16px;
+ font-size: 12px;
+ white-space: nowrap;
+}
+.htlRoomCardAmen > span {
+ flex: 0 0 auto;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border: 1px solid #D6D6D6;
+ border-radius: 50%;
+ font-size: 14px;
+ color: #666;
+}
+.htlRoomCardFoot {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border-top: 1px solid #E5E5E5;
+ gap: 10px;
+ padding-top: 14px;
+}
+.htlRoomCardFoot .htlRoomTypePriceText {
+ float: none;
+ margin: 0;
+ font-size: 22px;
+ font-weight: 700;
+ line-height: 1.2;
+}
+.htlRoomCardFoot .wk_roomType_price_type {
+ font-size: 14px;
+ font-weight: 400;
+ color: #777;
+}
+.htlRoomCardFoot .room_type_old_price {
+ display: block;
+ width: auto;
+ font-size: 14px;
+ font-weight: 400;
+}
+a.htlRoomTypeBookNow {
+ padding: 10px 20px;
+ border: 0;
+ border-radius: 3px;
+ background: #379BF0;
+ color: #fff;
+ font-weight: 700;
+ white-space: nowrap;
+}
+a.htlRoomTypeBookNow:hover {
+ background: #2d86d3;
+ color: #fff;
+}
+.htlRoom-owlCarousel .owl-nav .owl-prev,
+.htlRoom-owlCarousel .owl-nav .owl-next {
+ position: absolute;
+ top: 38%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ width: 44px;
+ height: 44px;
+ border-radius: 50%;
+ background: #fff;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, .2);
+ font-size: 22px;
+ line-height: 1;
+}
+.htlRoom-owlCarousel .owl-nav .owl-prev {
+ left: -15px;
+}
+.htlRoom-owlCarousel .owl-nav .owl-next {
+ right: -12px;
+}
+.htlRoom-owlCarousel .owl-nav .disabled {
+ display: none;
+}
+@media (max-width: 767px) {
+ .htlRoomCardBody .htlRoomTypeNameText {
+ font-size: 20px;
+ }
+}
+
+/* equal height cards: price row sits at the bottom, occupancy row keeps the same height with or without amenities */
+.htlRoom-owlCarousel .owl-stage {
+ display: flex;
+}
+.htlRoom-owlCarousel .owl-item {
+ display: flex;
+}
+.htlRoomCard {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+}
+.htlRoomCardBody {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+}
+.htlRoomCardFoot {
+ margin-top: auto;
+}
+.htlRoomCardGuests {
+ display: inline-flex;
+ align-items: center;
+ min-height: 32px;
+}
+
+.htlRoomCard {
+ width: calc(100% - 4px);
+ margin: 2px;
+}
+.htlRoom-owlCarousel .owl-nav > div i {
+ position: relative;
+ left: 1px;
+}
+
+/* no line right under the room name */
+.htlRoomTypeNameText + .htlRoomCardMeta {
+ border-top: 0;
+ padding-top: 12px;
+}
+
+.htlRoomCardFeatured .htlRoomFeatureMore {
+ margin-left: 5px;
+}
diff --git a/modules/wkhotelroom/views/js/WkHotelRoomBlockFront.js b/modules/wkhotelroom/views/js/WkHotelRoomBlockFront.js
index 216c2be8c..d921dcbac 100644
--- a/modules/wkhotelroom/views/js/WkHotelRoomBlockFront.js
+++ b/modules/wkhotelroom/views/js/WkHotelRoomBlockFront.js
@@ -20,7 +20,89 @@
* @license https://opensource.org/license/osl-3-0-php Open Software License version 3.0
*/
+/**
+ * Keeps the items of a room card row (featured features, amenity icons) on one line. Items that do not fit are
+ * hidden whole, so a lone separator or icon is never left at the end, and a "more" marker is added at the end.
+ *
+ * @param {jQuery} $slider slider holding the room cards
+ * @param {Object} row
+ * @param {string} row.selector the row element
+ * @param {string} row.more class of the "more" marker
+ * @param {function(number): string} row.moreText text of the marker, gets the number of hidden items
+ * @param {boolean} [row.partial] show the first hidden item shortened with an ellipsis when enough room is left
+ */
+function fitRoomCardRow($slider, row) {
+ var MIN_PARTIAL_WIDTH = 100; // narrowest width a shortened item is shown in
+
+ $slider.find(row.selector).each(function() {
+ var $row = $(this);
+ var $items = $row.children(':not(.' + row.more + ')').css({display: '', maxWidth: ''});
+ var fits = function() {
+ return $row.prop('scrollWidth') <= $row.innerWidth();
+ };
+
+ $row.children('.' + row.more).remove();
+ if (fits()) {
+ return;
+ }
+
+ // the marker takes room too, so it is added before the items are hidden
+ var $more = $(' ').appendTo($row);
+ var shown = $items.length;
+
+ while (shown > 1 && !fits()) {
+ $items.eq(--shown).hide();
+ $more.html(row.moreText($items.length - shown));
+ }
+
+ var $next = $items.eq(shown);
+ var $last = $items.eq(shown - 1);
+ var room = Math.floor($row.offset().left + $row.innerWidth() - ($last.offset().left + $last.outerWidth(true)));
+
+ if (row.partial && $next.length && room >= MIN_PARTIAL_WIDTH) {
+ $more.remove();
+ $next.css({display: 'inline-block', maxWidth: room - 4, overflow: 'hidden', textOverflow: 'ellipsis'});
+ }
+ });
+}
+
+/** Fits the featured features and the amenity icons of every room card of the slider. */
+function fitRoomCards($slider) {
+ fitRoomCardRow($slider, {
+ selector: '.htlRoomCardFeatured',
+ more: 'htlRoomFeatureMore',
+ moreText: function() { return '…'; },
+ partial: true
+ });
+ fitRoomCardRow($slider, {
+ selector: '.htlRoomCardAmen',
+ more: 'htlRoomAmenMore',
+ moreText: function(hidden) { return '+' + hidden; }
+ });
+}
+
$(document).ready(function() {
+ $('.htlRoom-owlCarousel .owl-carousel').on('initialized.owl.carousel refreshed.owl.carousel resized.owl.carousel', function() {
+ fitRoomCards($(this));
+ }).owlCarousel({
+ loop: true,
+ nav: true,
+ navText: [' ', ' '],
+ dots: false,
+ autoplay: true,
+ autoplaySpeed: 1000,
+ margin: 24,
+ items: 1,
+ rtl: typeof language_is_rtl != 'undefined' && language_is_rtl,
+ responsive: {
+ 768: { items: 2 },
+ 992: { items: 3 }
+ }
+ });
+ // fonts and icons can change the widths after the page is shown
+ $(window).on('load', function() {
+ fitRoomCards($('.htlRoom-owlCarousel .owl-carousel'));
+ });
applyChangesToDescription();
$(window).on('resize', function() {
resetDescriptionChanges();
diff --git a/modules/wkhotelroom/views/templates/hook/hotelRoomDisplayBlock.tpl b/modules/wkhotelroom/views/templates/hook/hotelRoomDisplayBlock.tpl
index 872d29a25..b63d3bcd1 100644
--- a/modules/wkhotelroom/views/templates/hook/hotelRoomDisplayBlock.tpl
+++ b/modules/wkhotelroom/views/templates/hook/hotelRoomDisplayBlock.tpl
@@ -38,64 +38,72 @@
{/if}
{block name='hotel_room_block_content'}
-
+
- {assign var='htlRoomBlockIteration' value=0}
- {foreach from=$hotelRoomDisplay item=roomDisplay name=htlRoom}
- {if $smarty.foreach.htlRoom.iteration%2}
-
- {/if}
-
- {block name='hotel_room_block_room_type_image'}
-
-
-
+
+ {foreach from=$hotelRoomDisplay item=roomDisplay name=htlRoom}
+
+ {block name='hotel_room_block_room_type_image'}
+
+
+
+ {/block}
+ {block name='displayHotelRoomsBlockImageAfter'}
+ {hook h='displayHotelRoomsBlockImageAfter' room_type=$roomDisplay}
+ {/block}
+
+ {block name='hotel_room_block_room_type_description'}
+
{$roomDisplay.name|escape:'htmlall':'UTF-8'}
+ {if $roomDisplay.feature}
+
+ {* one line: the row is cut with an ellipsis by the css when it is wider than the card *}
+ {foreach from=$roomDisplay.feature item=feat}
+
+ {if $feat.logo_type == 'icon'} {else} {/if}
+ {$feat.value|escape:'htmlall':'UTF-8'}
+
+ {/foreach}
+
+ {/if}
+ {if $roomDisplay.amenities}
+
+ {/if}
{/block}
- {block name='displayHotelRoomsBlockImageAfter'}
- {hook h='displayHotelRoomsBlockImageAfter' room_type=$roomDisplay}
- {/block}
-
- {block name='hotel_room_block_room_type_description'}
-
-
{$roomDisplay.name|escape:'htmlall':'UTF-8'}
- {if $roomDisplay.show_price && !isset($restricted_country_mode) && !$PS_CATALOG_MODE}
-
- {if $roomDisplay.feature_price_diff >= 0}
- {convertPrice price = $roomDisplay.price_without_reduction}
- {/if}
- {if $roomDisplay.feature_price_diff}
- {convertPrice price = $roomDisplay.feature_price}
- {/if}
-
- / {l s='Per Night' mod='wkhotelroom'}
-
-
+
-
{$roomDisplay.description|escape:'html':'UTF-8'}
-
{$roomDisplay.description|escape:'html':'UTF-8'}
-
- {/block}
+ {if $roomDisplay.feature_price_diff}
+
{convertPrice price = $roomDisplay.feature_price}
+ {/if}
+
/ {l s='night' mod='wkhotelroom'}
+
+ {/if}
{block name='hotel_room_block_action'}
-
+
{if !isset($restricted_country_mode) && !$PS_CATALOG_MODE}{l s='Book now' mod='wkhotelroom'}{else}{l s='View' mod='wkhotelroom'}{/if}
{/block}
- {if !($smarty.foreach.htlRoom.iteration%2)}
- {/if}
- {assign var='htlRoomBlockIteration' value=$smarty.foreach.htlRoom.iteration}
- {/foreach}
- {if $htlRoomBlockIteration%2}
-
- {/if}
+ {/foreach}
+
{/block}
-
+
{/if}
{/block}
diff --git a/modules/wkhotelroom/wkhotelroom.php b/modules/wkhotelroom/wkhotelroom.php
index c38a97d98..56abb1a23 100644
--- a/modules/wkhotelroom/wkhotelroom.php
+++ b/modules/wkhotelroom/wkhotelroom.php
@@ -51,6 +51,8 @@ class WkHotelRoom extends Module
$dateFrom = date('Y-m-d');
$dateTo = date('Y-m-d', strtotime($dateFrom) + 86400);
$useTax = HotelBookingDetail::useTax();
+ $maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_ROOM_SEARCH');
+ $maxFeatureValues = (int) Configuration::get('WK_MAX_ROOM_TYPE_FEATURE_VALUES');
foreach ($hotelRoomDisplay as &$htlRoom) {
$idProduct = $htlRoom['id_product'];
$product = new Product($idProduct, false, $idLang);
@@ -109,6 +111,23 @@ class WkHotelRoom extends Module
1,
array(array('adults' => 1, 'children' => 0, 'child_ages' => array()))
);
+ $objRoomType = new HotelRoomType();
+ $roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($idProduct);
+ $htlRoom['max_adults'] = $roomTypeInfo ? (int) $roomTypeInfo['max_adults'] : 0;
+ $htlRoom['max_children'] = $roomTypeInfo ? (int) $roomTypeInfo['max_children'] : 0;
+ $features = array();
+ foreach (Product::getFrontFeaturesStatic($idLang, $idProduct) as $feature) {
+ if ($feature['is_featured'] && $feature['logo']) {
+ $features[$feature['id_feature']] = $features[$feature['id_feature']] ?? $feature;
+ $features[$feature['id_feature']]['values'][] = $feature['value'];
+ }
+ }
+ $htlRoom['feature'] = array_slice($features, 0, $maxFeatures ?: null);
+ foreach ($htlRoom['feature'] as &$roomFeature) {
+ $roomFeature['value'] = implode(', ', array_slice($roomFeature['values'], 0, $maxFeatureValues ?: null));
+ }
+ unset($roomFeature);
+ $htlRoom['amenities'] = HotelRoomTypeAmenities::getAmenities($idProduct, $idLang, true);
$htlRoom['feature_price'] = $featurePrice;
$htlRoom['feature_price_diff'] = (float)($priceWithoutReduction - $featurePrice);
}
@@ -123,10 +142,14 @@ class WkHotelRoom extends Module
'HOTEL_ROOM_DISPLAY_DESCRIPTION',
$this->context->language->id
),
+ 'amenity_img_dir' => _MODULE_DIR_.'hotelreservationsystem/views/img/hotel_amenities/',
'hotelRoomDisplay' => $hotelRoomDisplay
)
);
+ $this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.carousel.min.css');
+ $this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.theme.default.min.css');
+ $this->context->controller->addJS(_PS_JS_DIR_.'owl-carousel/owl.carousel.min.js');
$this->context->controller->addJs($this->_path.'/views/js/WkHotelRoomBlockFront.js');
$this->context->controller->addCSS($this->_path.'/views/css/WkHotelRoomBlockFront.css');
diff --git a/modules/wkroomsearchblock/views/css/wk-category-search.css b/modules/wkroomsearchblock/views/css/wk-category-search.css
index 37889ddd2..b974b57c0 100644
--- a/modules/wkroomsearchblock/views/css/wk-category-search.css
+++ b/modules/wkroomsearchblock/views/css/wk-category-search.css
@@ -60,4 +60,54 @@
.header-rmsearch-wrapper {
padding: 10px 5px;}
+}
+
+/* searched details and the close icon are for mobile and tablet only */
+.header-rmsearch-details-wrapper,
+.header-rmsearch-wrapper .close_room_serach_wrapper {
+ display: none;}
+/* mobile and tablet: show the searched details, the search form opens with the edit button */
+@media (max-width: 991px) {
+ .header-rmsearch-wrapper {
+ display: none;
+ position: relative;}
+ .header-rmsearch-details-wrapper {
+ display: block;}
+ .header-rmsearch-wrapper .close_room_serach_wrapper {
+ display: block;
+ position: absolute;
+ top: 8px;
+ right: 15px;
+ z-index: 1;}
+ .close_room_serach_wrapper img {
+ width: 18px;}
+ .header-rmsearch-details-wrapper {
+ padding: 0 0 15px;}
+ .header-rmsearch-details-wrapper .filter_header {
+ font-size: 12px;
+ color: #333;
+ line-height: 25px;}
+ .header-rmsearch-details-wrapper .filter_header p {
+ margin: 0;}
+ .header-rmsearch-details-wrapper .search_result_info {
+ font-size: 15px;
+ font-weight: bold;
+ line-height: 30px;
+ color: #333333;}
+ .header-rmsearch-details-wrapper .modify_roomtype_search_btn {
+ border: 1px solid #777777;
+ font-weight: bold;
+ border-radius: 4px;
+ font-size: 13px;
+ padding: 6px 12px;
+ color: #333333;
+ text-align: center;
+ text-transform: uppercase;}
+ .header-rmsearch-details-wrapper .faded-txt {
+ color: #777777;}
+}
+/* desktop always shows the search form, even if it was closed on a smaller screen */
+@media (min-width: 992px) {
+ .header-rmsearch-wrapper {
+ display: block !important;}
}
\ No newline at end of file
diff --git a/modules/wkroomsearchblock/views/css/wk-landing-page-search.css b/modules/wkroomsearchblock/views/css/wk-landing-page-search.css
index 63c48a472..d4ebcab33 100644
--- a/modules/wkroomsearchblock/views/css/wk-landing-page-search.css
+++ b/modules/wkroomsearchblock/views/css/wk-landing-page-search.css
@@ -60,8 +60,14 @@
column-gap: 0px;
}
.header-rmsearch-wrapper {
- height: calc(100vw - 60px);
+ height: auto;
width: calc(100vw - 60px);
+ max-height: 80vh;
+ overflow-y: auto;
+ }
+ /* the date picker opens inside this popup, so don't clip it */
+ .fancybox-inner:has(#xs_room_search_form) {
+ overflow: visible !important;
}
}
.fancy_search_header_xs {
diff --git a/modules/wkroomsearchblock/views/templates/hook/categoryPageSearch.tpl b/modules/wkroomsearchblock/views/templates/hook/categoryPageSearch.tpl
index e0a1037a5..59cedeb92 100644
--- a/modules/wkroomsearchblock/views/templates/hook/categoryPageSearch.tpl
+++ b/modules/wkroomsearchblock/views/templates/hook/categoryPageSearch.tpl
@@ -22,7 +22,23 @@
{block name='category_page_search_panel'}
{if isset($hotels_info) && count($hotels_info)}
+ {* searched information, shown on mobile and tablet only *}
+ {block name='category_page_search_info'}
+ {if isset($search_data) && $search_data}
+
+ {/if}
+ {/block}