stay_commerce-frontend 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/builds/404error-RVKQJ4S4.digested.jpg +0 -0
  6. data/app/assets/builds/Facebook-5HJUILVR.digested.svg +3 -0
  7. data/app/assets/builds/Google-CZ3UPVSC.digested.svg +6 -0
  8. data/app/assets/builds/application.js +95 -0
  9. data/app/assets/builds/application.js.map +7 -0
  10. data/app/assets/builds/beach-KAZW5GM3.digested.jpg +0 -0
  11. data/app/assets/builds/beach2-3ARC34NS.digested.jpg +0 -0
  12. data/app/assets/builds/beach3-PSTOH5FV.digested.jpg +0 -0
  13. data/app/assets/builds/beach4-PWNRPD3S.digested.jpg +0 -0
  14. data/app/assets/builds/beach5-SBOKKRHF.digested.jpg +0 -0
  15. data/app/assets/builds/beach6-SXZ5Y5AI.digested.jpg +0 -0
  16. data/app/assets/builds/beach7-FNBMFVKK.digested.jpg +0 -0
  17. data/app/assets/builds/beach8-TD7LTRQM.digested.jpg +0 -0
  18. data/app/assets/builds/bg-image-H7UGUMV2.digested.jpg +0 -0
  19. data/app/assets/builds/bgimage-DU3PCXKN.digested.jpg +0 -0
  20. data/app/assets/builds/bundle.css +30914 -0
  21. data/app/assets/builds/bundle.css.map +7 -0
  22. data/app/assets/builds/bundle.js +74569 -0
  23. data/app/assets/builds/bundle.js.map +7 -0
  24. data/app/assets/builds/hotel1-HGBXPKJK.digested.jpg +0 -0
  25. data/app/assets/builds/logo_updated-KQWAXLYL.digested.png +0 -0
  26. data/app/assets/builds/styles.css +6422 -0
  27. data/app/assets/builds/styles.css.map +7 -0
  28. data/app/assets/config/manifest.js +2 -0
  29. data/app/assets/images/favicon.ico +0 -0
  30. data/app/assets/images/stay_commerce/frontend/beach-666122_1280 (1).jpg +0 -0
  31. data/app/assets/images/stay_commerce/frontend/beach-666122_1280.jpg +0 -0
  32. data/app/assets/stylesheets/stay_commerce/frontend/application.css +15 -0
  33. data/app/controllers/stay_commerce/frontend/application_controller.rb +8 -0
  34. data/app/controllers/stay_commerce/frontend/welcome_controller.rb +6 -0
  35. data/app/helpers/stay_commerce/frontend/application_helper.rb +6 -0
  36. data/app/javascript/Images/404error.jpg +0 -0
  37. data/app/javascript/Images/Facebook.svg +3 -0
  38. data/app/javascript/Images/Google.svg +6 -0
  39. data/app/javascript/Images/beach-2245867_1280.jpg +0 -0
  40. data/app/javascript/Images/beach.jpg +0 -0
  41. data/app/javascript/Images/beach2.jpg +0 -0
  42. data/app/javascript/Images/beach3.jpg +0 -0
  43. data/app/javascript/Images/beach4.jpg +0 -0
  44. data/app/javascript/Images/beach5.jpg +0 -0
  45. data/app/javascript/Images/beach6.jpg +0 -0
  46. data/app/javascript/Images/beach7.jpg +0 -0
  47. data/app/javascript/Images/beach8.jpg +0 -0
  48. data/app/javascript/Images/bg-image.jpg +0 -0
  49. data/app/javascript/Images/bgimage.jpg +0 -0
  50. data/app/javascript/Images/blog-1.jpg +0 -0
  51. data/app/javascript/Images/gallery.png +0 -0
  52. data/app/javascript/Images/home 5.jpg +0 -0
  53. data/app/javascript/Images/home1.jpg +0 -0
  54. data/app/javascript/Images/home2.jpg +0 -0
  55. data/app/javascript/Images/home3.jpg +0 -0
  56. data/app/javascript/Images/home6.jpg +0 -0
  57. data/app/javascript/Images/hotel1.jpg +0 -0
  58. data/app/javascript/Images/room1.jpg +0 -0
  59. data/app/javascript/Images/room2.jpg +0 -0
  60. data/app/javascript/Images/room3.jpg +0 -0
  61. data/app/javascript/Images/room4.jpg +0 -0
  62. data/app/javascript/Images/wine-4520213_1280.jpg +0 -0
  63. data/app/javascript/application.js +1 -0
  64. data/app/javascript/react/Api/apiConstants.js +47 -0
  65. data/app/javascript/react/assets/beach-2245867_1280.jpg +0 -0
  66. data/app/javascript/react/assets/logo.png +0 -0
  67. data/app/javascript/react/assets/logo_updated.png +0 -0
  68. data/app/javascript/react/components/AboutUsPage/AboutPage.jsx +41 -0
  69. data/app/javascript/react/components/AboutusFront/About.jsx +37 -0
  70. data/app/javascript/react/components/Accommodation/Accommodation.jsx +32 -0
  71. data/app/javascript/react/components/Accommodation/NormalListing.jsx +50 -0
  72. data/app/javascript/react/components/Accommodation/SpecialListing.jsx +51 -0
  73. data/app/javascript/react/components/Accountpage/AccountInfo.jsx +217 -0
  74. data/app/javascript/react/components/Accountpage/AccountPage.jsx +27 -0
  75. data/app/javascript/react/components/Accountpage/CommonPage.jsx +24 -0
  76. data/app/javascript/react/components/AddNewProperty/CommonLayout.jsx +68 -0
  77. data/app/javascript/react/components/AddNewProperty/Description.jsx +229 -0
  78. data/app/javascript/react/components/AddNewProperty/Details.jsx +234 -0
  79. data/app/javascript/react/components/AddNewProperty/Images.jsx +196 -0
  80. data/app/javascript/react/components/AddNewProperty/Location.jsx +239 -0
  81. data/app/javascript/react/components/AddNewProperty/Room.jsx +1132 -0
  82. data/app/javascript/react/components/AvatarDropdown/AvatarDropDown.jsx +142 -0
  83. data/app/javascript/react/components/BlogDesign/BlogsLatest.jsx +67 -0
  84. data/app/javascript/react/components/ContactUs/Contact.jsx +89 -0
  85. data/app/javascript/react/components/FacilitiesSection/Facilities.jsx +66 -0
  86. data/app/javascript/react/components/FixedNavbar/FixedNav.jsx +88 -0
  87. data/app/javascript/react/components/ForgetPassword/ForgetPassword.jsx +103 -0
  88. data/app/javascript/react/components/Gallery/Gallery.jsx +164 -0
  89. data/app/javascript/react/components/GalleryModalLight/GalleryModalLight.jsx +35 -0
  90. data/app/javascript/react/components/GallerySlider/GallerySlider.jsx +58 -0
  91. data/app/javascript/react/components/Headers/PropertyCard.jsx +46 -0
  92. data/app/javascript/react/components/HeroSectionDesign/BookingForm.jsx +178 -0
  93. data/app/javascript/react/components/HeroSectionDesign/HeroSection.jsx +29 -0
  94. data/app/javascript/react/components/HeroSectionDesign/MyPropertiesListing.jsx +104 -0
  95. data/app/javascript/react/components/HeroSectionDesign/PropertiesPage.jsx +122 -0
  96. data/app/javascript/react/components/HotelListing/Listing.jsx +48 -0
  97. data/app/javascript/react/components/Layout/Layout.js +18 -0
  98. data/app/javascript/react/components/Listing-stay-Detail/AmenitiesFeatures.jsx +58 -0
  99. data/app/javascript/react/components/Listing-stay-Detail/AmenitiesModal.jsx +250 -0
  100. data/app/javascript/react/components/Listing-stay-Detail/ApartmentCard.jsx +120 -0
  101. data/app/javascript/react/components/Listing-stay-Detail/BookingModal.jsx +398 -0
  102. data/app/javascript/react/components/Listing-stay-Detail/CheckoutForm.jsx +296 -0
  103. data/app/javascript/react/components/Listing-stay-Detail/ListingStayDetailPage.jsx +512 -0
  104. data/app/javascript/react/components/Listing-stay-Detail/PropertyDescription.jsx +76 -0
  105. data/app/javascript/react/components/Listing-stay-Detail/PropertyDetailsCard.jsx +62 -0
  106. data/app/javascript/react/components/Listing-stay-Detail/Reviews.jsx +132 -0
  107. data/app/javascript/react/components/Listing-stay-Detail/RoomDescriptionModal.jsx +105 -0
  108. data/app/javascript/react/components/Listing-stay-Detail/Rules.jsx +23 -0
  109. data/app/javascript/react/components/ListingImageGallery/ListingImageGallery.jsx +30 -0
  110. data/app/javascript/react/components/LoginPage/LoginPage.jsx +115 -0
  111. data/app/javascript/react/components/MobileNav/MobileMenu.jsx +47 -0
  112. data/app/javascript/react/components/Navbar/Navbar.jsx +25 -0
  113. data/app/javascript/react/components/Page404/Page404.jsx +30 -0
  114. data/app/javascript/react/components/PropertyListing/MyProperties.jsx +146 -0
  115. data/app/javascript/react/components/PropertyListing/StayBooking/BookingDetails.jsx +178 -0
  116. data/app/javascript/react/components/PropertyListing/StayBooking/MyBooking.jsx +83 -0
  117. data/app/javascript/react/components/ResetPassword/ResetPassword.jsx +117 -0
  118. data/app/javascript/react/components/SignupPage/SignupPage.jsx +185 -0
  119. data/app/javascript/react/components/SmallNavbar/SmallNav.jsx +51 -0
  120. data/app/javascript/react/components/SocialAuth/SocialAuth.jsx +21 -0
  121. data/app/javascript/react/components/StayCard/StayCard.jsx +69 -0
  122. data/app/javascript/react/components/StayCard/StayCard2.jsx +45 -0
  123. data/app/javascript/react/components/StayCard/StayCard3.jsx +45 -0
  124. data/app/javascript/react/components/TestimonialSection/Testimonial.jsx +113 -0
  125. data/app/javascript/react/components/Unauthorized/Unauthorized.jsx +12 -0
  126. data/app/javascript/react/data/jsons/__countryListing.json +201 -0
  127. data/app/javascript/react/packs/App.js +26 -0
  128. data/app/javascript/react/packs/index.jsx +38 -0
  129. data/app/javascript/react/packs/routes/ParentRoute.jsx +14 -0
  130. data/app/javascript/react/packs/routes/Route.jsx +163 -0
  131. data/app/javascript/react/pages/AccommodationList.jsx +21 -0
  132. data/app/javascript/react/pages/Home.jsx +32 -0
  133. data/app/javascript/react/redux/slices/AuthSlice/AuthSlice.jsx +100 -0
  134. data/app/javascript/react/redux/slices/PropertySlice/PropertySlice.jsx +722 -0
  135. data/app/javascript/react/redux/slices/PropertySlice/Searchslice.jsx +36 -0
  136. data/app/javascript/react/redux/slices/UserSlice/UserSlice.jsx +215 -0
  137. data/app/javascript/react/redux/store.js +35 -0
  138. data/app/javascript/react/shared/Avatar/Avatar.jsx +32 -0
  139. data/app/javascript/react/shared/Badge/Badge.jsx +33 -0
  140. data/app/javascript/react/shared/Button/Button.jsx +67 -0
  141. data/app/javascript/react/shared/Button/ButtonPrimary.jsx +11 -0
  142. data/app/javascript/react/shared/Button/ButtonSelect.jsx +9 -0
  143. data/app/javascript/react/shared/Checkbox/Checkbox.jsx +38 -0
  144. data/app/javascript/react/shared/CurrencySymbol.jsx +6 -0
  145. data/app/javascript/react/shared/DateField/CustomDatePicker.jsx +69 -0
  146. data/app/javascript/react/shared/FooterSection/Footer.jsx +75 -0
  147. data/app/javascript/react/shared/FormField/FormField.jsx +75 -0
  148. data/app/javascript/react/shared/FormItem/FormItem.jsx +20 -0
  149. data/app/javascript/react/shared/Input/Input.jsx +27 -0
  150. data/app/javascript/react/shared/Label/Label.jsx +12 -0
  151. data/app/javascript/react/shared/Modal.jsx +20 -0
  152. data/app/javascript/react/shared/NcImage/NcImage.jsx +101 -0
  153. data/app/javascript/react/shared/NcImage/PlaceIcon.jsx +31 -0
  154. data/app/javascript/react/shared/NcImage/placecImageIcon.svg +6 -0
  155. data/app/javascript/react/shared/Select/Select.jsx +20 -0
  156. data/app/javascript/react/styles/404error.scss +58 -0
  157. data/app/javascript/react/styles/ApartmentCard.scss +126 -0
  158. data/app/javascript/react/styles/BookingDetails.scss +457 -0
  159. data/app/javascript/react/styles/Modal.scss +36 -0
  160. data/app/javascript/react/styles/PropertiesPage.scss +219 -0
  161. data/app/javascript/react/styles/RenderSection.scss +480 -0
  162. data/app/javascript/react/styles/about.scss +97 -0
  163. data/app/javascript/react/styles/accountinfo.scss +67 -0
  164. data/app/javascript/react/styles/accountpage.scss +36 -0
  165. data/app/javascript/react/styles/amenitiesfeatures.scss +223 -0
  166. data/app/javascript/react/styles/application.scss +87 -0
  167. data/app/javascript/react/styles/avatar.scss +39 -0
  168. data/app/javascript/react/styles/avatardropdown.scss +57 -0
  169. data/app/javascript/react/styles/badge.scss +90 -0
  170. data/app/javascript/react/styles/blog.scss +100 -0
  171. data/app/javascript/react/styles/bookingform.scss +124 -0
  172. data/app/javascript/react/styles/button.scss +44 -0
  173. data/app/javascript/react/styles/buttonprimary.scss +32 -0
  174. data/app/javascript/react/styles/checkbox.scss +37 -0
  175. data/app/javascript/react/styles/commonlayout.scss +83 -0
  176. data/app/javascript/react/styles/commonpage.scss +51 -0
  177. data/app/javascript/react/styles/contact.scss +173 -0
  178. data/app/javascript/react/styles/customdatepicker.scss +120 -0
  179. data/app/javascript/react/styles/description.scss +21 -0
  180. data/app/javascript/react/styles/details.scss +88 -0
  181. data/app/javascript/react/styles/facilities.scss +131 -0
  182. data/app/javascript/react/styles/fixednavbar.scss +137 -0
  183. data/app/javascript/react/styles/fonts.scss +22 -0
  184. data/app/javascript/react/styles/footer.scss +300 -0
  185. data/app/javascript/react/styles/forgetpassword.scss +68 -0
  186. data/app/javascript/react/styles/formfield.scss +39 -0
  187. data/app/javascript/react/styles/formitem.scss +20 -0
  188. data/app/javascript/react/styles/gallery.scss +142 -0
  189. data/app/javascript/react/styles/gallerymodallight.scss +137 -0
  190. data/app/javascript/react/styles/galleryslider.scss +114 -0
  191. data/app/javascript/react/styles/header.scss +49 -0
  192. data/app/javascript/react/styles/herosection.scss +61 -0
  193. data/app/javascript/react/styles/images.scss +112 -0
  194. data/app/javascript/react/styles/input.scss +58 -0
  195. data/app/javascript/react/styles/label.scss +11 -0
  196. data/app/javascript/react/styles/listing.scss +94 -0
  197. data/app/javascript/react/styles/listingimagegallery.scss +57 -0
  198. data/app/javascript/react/styles/listingstaydetailpage.scss +887 -0
  199. data/app/javascript/react/styles/location.scss +66 -0
  200. data/app/javascript/react/styles/loginpage.scss +150 -0
  201. data/app/javascript/react/styles/mobilemenu.scss +53 -0
  202. data/app/javascript/react/styles/mybooking.scss +104 -0
  203. data/app/javascript/react/styles/myproperty.scss +51 -0
  204. data/app/javascript/react/styles/ncimage.scss +24 -0
  205. data/app/javascript/react/styles/normallisting.scss +95 -0
  206. data/app/javascript/react/styles/property-description.scss +75 -0
  207. data/app/javascript/react/styles/propertycard.scss +48 -0
  208. data/app/javascript/react/styles/propertydetailscard.scss +302 -0
  209. data/app/javascript/react/styles/resetpassword.scss +79 -0
  210. data/app/javascript/react/styles/reviews.scss +185 -0
  211. data/app/javascript/react/styles/room.scss +275 -0
  212. data/app/javascript/react/styles/rooms.scss +0 -0
  213. data/app/javascript/react/styles/select.scss +44 -0
  214. data/app/javascript/react/styles/signuppage.scss +132 -0
  215. data/app/javascript/react/styles/smallnav.scss +94 -0
  216. data/app/javascript/react/styles/socialauth.scss +62 -0
  217. data/app/javascript/react/styles/speciallisting.scss +94 -0
  218. data/app/javascript/react/styles/staycard.scss +77 -0
  219. data/app/javascript/react/styles/staycard2.scss +115 -0
  220. data/app/javascript/react/styles/testimonial.scss +216 -0
  221. data/app/javascript/react/styles/unauthorized.scss +22 -0
  222. data/app/javascript/react/styles/variables.scss +3 -0
  223. data/app/javascript/react/styles/wrapper.scss +4 -0
  224. data/app/javascript/react/utils/formSchema.js +120 -0
  225. data/app/javascript/react/utils/helpers/APIHelper.jsx +55 -0
  226. data/app/javascript/react/utils/helpers/ErrorHandler.js +21 -0
  227. data/app/javascript/react/utils/helpers/InfoHandler.js +15 -0
  228. data/app/javascript/react/utils/helpers/SuccessHandler.js +12 -0
  229. data/app/javascript/react/utils/helpers/isInViewPortIntersectionObserver.jsx +39 -0
  230. data/app/jobs/stay_commerce/frontend/application_job.rb +6 -0
  231. data/app/mailers/stay_commerce/frontend/application_mailer.rb +8 -0
  232. data/app/models/stay_commerce/frontend/application_record.rb +7 -0
  233. data/app/views/layouts/stay_commerce/frontend/application.html.erb +61 -0
  234. data/app/views/stay_commerce/frontend/welcome/index.html.erb +2 -0
  235. data/config/initializers/cors.rb +6 -0
  236. data/config/initializers/devise.rb +359 -0
  237. data/config/routes.rb +11 -0
  238. data/lib/stay_commerce/frontend/engine.rb +14 -0
  239. data/lib/stay_commerce/frontend/version.rb +5 -0
  240. data/lib/stay_commerce/frontend.rb +8 -0
  241. data/lib/tasks/stay_commerce/frontend_tasks.rake +4 -0
  242. metadata +370 -0
@@ -0,0 +1,512 @@
1
+ import React, { useCallback, useEffect, useState } from "react";
2
+ import { LazyLoadImage } from "react-lazy-load-image-component";
3
+ import { Squares2X2Icon } from "@heroicons/react/24/outline";
4
+ import { useFormik } from "formik";
5
+ import "../../styles/listingstaydetailpage.scss";
6
+ import moment from "moment";
7
+ import ButtonPrimary from "../../shared/Button/ButtonPrimary";
8
+ import {
9
+ getallupdateProperties,
10
+ reserveBooking,
11
+ } from "../../redux/slices/PropertySlice/PropertySlice";
12
+ import { useDispatch } from "react-redux";
13
+ import { Navigate, useParams } from "react-router-dom";
14
+ import PropertyDetailsCard from "./PropertyDetailsCard";
15
+ import ListingImageGallery from "../ListingImageGallery/ListingImageGallery";
16
+ import GallerySlider from "../GallerySlider/GallerySlider";
17
+ import Badge from ".././../shared/Badge/Badge";
18
+ import { Tooltip } from "react-tooltip";
19
+ import { currencySymbol } from "../../shared/CurrencySymbol";
20
+ import "../../styles/RenderSection.scss";
21
+ import { useNavigate } from "react-router-dom";
22
+ import successHandler from "../../utils/helpers/SuccessHandler";
23
+ import { User, DoorOpen, Bed, Expand } from "lucide-react";
24
+ import BookingModal from "./BookingModal";
25
+ import AmenitiesModal from "./AmenitiesModal";
26
+ import RoomDescriptionModal from "./RoomDescriptionModal";
27
+
28
+ const StayDetailPageContainer = () => {
29
+ const dispatch = useDispatch();
30
+ const [propertyData, setPropertyData] = useState(null);
31
+ const { slug } = useParams();
32
+ const propertyId = slug;
33
+ const images =
34
+ propertyData?.rooms?.flatMap(
35
+ (room) => room.room_images?.map((url) => ({ url })) || []
36
+ ) || [];
37
+ const [imageModal, setIsOpenImageModal] = useState(false);
38
+ const handleOpenModalImageGallery = () => {
39
+ setIsOpenImageModal(true);
40
+ };
41
+ const [isBookingModalOpen, setIsBookingModalOpen] = useState(false);
42
+ const [selectedRoomForBooking, setSelectedRoomForBooking] = useState(null);
43
+ const [isAmenitiesModalOpen, setIsAmenitiesModalOpen] = useState(false);
44
+ const [selectedRoomForAmenities, setSelectedRoomForAmenities] =
45
+ useState(null);
46
+
47
+ const closeImageModal = () => {
48
+ setIsOpenImageModal(false);
49
+ };
50
+
51
+ const fetchPropertyData = useCallback(async () => {
52
+ try {
53
+ const response = await dispatch(getallupdateProperties({ propertyId }));
54
+
55
+ const property = response?.payload;
56
+
57
+ if (property) {
58
+ setPropertyData(property);
59
+ } else {
60
+ console.error("Property not found:", response);
61
+ }
62
+ } catch (error) {
63
+ console.error("Error fetching property data:", error);
64
+ }
65
+ }, [dispatch, propertyId]);
66
+
67
+ useEffect(() => {
68
+ if (propertyId) {
69
+ fetchPropertyData();
70
+ }
71
+ }, [fetchPropertyData, propertyId]);
72
+
73
+ const RenderSection = () => {
74
+ const navigate = useNavigate();
75
+ const [error, setError] = useState("");
76
+ const [isButtonDisabled, setIsButtonDisabled] = useState(true);
77
+ const [currentRoom, setCurrentRoom] = useState(null);
78
+ const [isLoading, setIsLoading] = useState(false);
79
+
80
+ const dispatch = useDispatch();
81
+ const propertyId = slug;
82
+ const maximumGuests = currentRoom && Number(currentRoom.max_guests);
83
+ const [selectedRoomForDesc, setSelectedRoomForDesc] = useState(null);
84
+
85
+ const formik = useFormik({
86
+ initialValues: {
87
+ number_of_guests: null,
88
+ booking_start: null,
89
+ booking_end: null,
90
+ room_id: null,
91
+ line_items_attributes: [],
92
+ price_per_month: null,
93
+ },
94
+ enableReinitialize: true,
95
+ onSubmit: (values) => {
96
+ handleSubmit(values);
97
+ },
98
+ });
99
+
100
+ const extraGuest =
101
+ formik.values.number_of_guests &&
102
+ formik.values.number_of_guests > maximumGuests &&
103
+ formik.values.number_of_guests - maximumGuests;
104
+ const calculateMonthsDifference = (bookingStart, bookingEnd) => {
105
+ const start = moment(bookingStart, "YYYY-MM-DD");
106
+ const end = moment(bookingEnd, "YYYY-MM-DD");
107
+ const uniqueMonths = new Set();
108
+ const MAX_CHAR_LIMIT = 250;
109
+ let current = start.clone();
110
+ while (current.isBefore(end, "day") || current.isSame(end, "day")) {
111
+ uniqueMonths.add(`${current.year()}-${current.month() + 1}`);
112
+ current.add(1, "day");
113
+ }
114
+
115
+ return uniqueMonths.size;
116
+ };
117
+
118
+ const handleSubmit = async (values) => {
119
+ const postData = {
120
+ booking: {
121
+ number_of_guests: Number(values.number_of_guests),
122
+ property_id: propertyId,
123
+ check_in_date: moment(values.booking_start).format("YYYY-MM-DD"),
124
+ check_out_date: moment(values.booking_end).format("YYYY-MM-DD"),
125
+ no_of_months: calculateMonthsDifference(
126
+ moment(values.booking_start).format("YYYY-MM-DD"),
127
+ moment(values.booking_end).format("YYYY-MM-DD")
128
+ ),
129
+ line_items_attributes: [
130
+ {
131
+ room_id: values.room_id,
132
+ price: values.price_per_month,
133
+ quantity: 1,
134
+ property_id: propertyId,
135
+ },
136
+ ],
137
+ },
138
+ };
139
+
140
+ setIsLoading(true);
141
+ try {
142
+ const action = await dispatch(reserveBooking(postData));
143
+ if (action?.type === "payment/reserveBooking/fulfilled" && action) {
144
+ successHandler(
145
+ "Booking request sent. Please ! wait for owner's confirmation!!"
146
+ );
147
+ const bookingId = action?.payload?.id;
148
+ if (bookingId) {
149
+ setError("");
150
+ setIsBookingModalOpen(false);
151
+ navigate(`/myreservations/${bookingId}`);
152
+ } else {
153
+ setError("Failed to reserve booking. Please try again.");
154
+ }
155
+ }
156
+ } catch (error) {
157
+ ErrorHandler("Failed to reserve booking. Please try again.");
158
+ } finally {
159
+ setIsLoading(false);
160
+ }
161
+ };
162
+
163
+ const openBookingModal = (room) => {
164
+ setSelectedRoomForBooking(room);
165
+ setIsBookingModalOpen(true);
166
+ };
167
+
168
+ // Function to close booking modal
169
+ const closeBookingModal = () => {
170
+ setIsBookingModalOpen(false);
171
+ setSelectedRoomForBooking(null);
172
+ setError("");
173
+ };
174
+
175
+ const openAmenitiesModal = (room) => {
176
+ setSelectedRoomForAmenities(room);
177
+ setIsAmenitiesModalOpen(true);
178
+ };
179
+
180
+ const closeAmenitiesModal = () => {
181
+ setIsAmenitiesModalOpen(false);
182
+ setSelectedRoomForAmenities(null);
183
+ };
184
+
185
+ const modifiedStatus = (status) => {
186
+ switch (status) {
187
+ case "active":
188
+ return "Active";
189
+ case "inactive":
190
+ return "Inactive";
191
+ default:
192
+ return "";
193
+ }
194
+ };
195
+
196
+ useEffect(() => {
197
+ const { booking_start, booking_end, number_of_guests } = formik.values;
198
+ if (booking_start && booking_end && number_of_guests) {
199
+ setIsButtonDisabled(false);
200
+ } else {
201
+ setIsButtonDisabled(true);
202
+ }
203
+ }, [formik.values]);
204
+ const selectedRoom = formik?.values?.room_id;
205
+ if (!propertyData) return null;
206
+
207
+ return (
208
+ <>
209
+ <h2 className="section-title">Rooms</h2>
210
+ {propertyData?.rooms?.map((room, index) => {
211
+ const features = room?.features || [];
212
+ const amenities = room?.amenities || [];
213
+ const totalItems = features.length + amenities.length;
214
+ const showViewAllButton = totalItems > 4;
215
+ const displayFeatures = showViewAllButton
216
+ ? features.slice(0, 2)
217
+ : features;
218
+ const displayAmenities = showViewAllButton
219
+ ? amenities.slice(0, 2)
220
+ : amenities;
221
+
222
+ return (
223
+ <div
224
+ key={room?.id}
225
+ className={`section-container ${
226
+ propertyData?.is_shared_property ? "shared-property" : ""
227
+ }`}
228
+ >
229
+ <div
230
+ className={`room-container ${
231
+ propertyData?.is_shared_property ? "shared-property" : ""
232
+ }`}
233
+ >
234
+ <header>
235
+ <div className="room-item-wrapper">
236
+ <div
237
+ className="room-image"
238
+ style={{ position: "relative" }}
239
+ >
240
+ <GallerySlider
241
+ ratioClass="gallery-aspect"
242
+ images={room?.room_images || []}
243
+ style={{
244
+ height: "370px",
245
+ borderTopLeftRadius: "20px",
246
+ borderBottomLeftRadius: "20px",
247
+ }}
248
+ />
249
+ <div
250
+ style={{
251
+ position: "absolute",
252
+ top: "12px",
253
+ left: "12px",
254
+ zIndex: 10,
255
+ }}
256
+ >
257
+ <Badge
258
+ name={modifiedStatus(room?.status) || "Property"}
259
+ />
260
+ </div>
261
+ </div>
262
+
263
+ <div style={{ width: "100%", padding: "20px 20px 0 0" }}>
264
+ <div className="room-content">
265
+ <div className="rooms-parents">
266
+ <div
267
+ style={{ display: "flex", alignItems: "center" }}
268
+ >
269
+ <h2 className="room-title">{room?.name}</h2>
270
+ </div>
271
+ </div>
272
+ <hr style={{ marginTop: 0 }} />
273
+
274
+ <div className="room-features">
275
+ <div className="feature-item">
276
+ <User className="icon" size={18} />
277
+ <span>{room?.max_guests || "Default Value"}</span>
278
+ </div>
279
+ <div className="feature-item">
280
+ <DoorOpen className="icon" size={18} />
281
+ <span>{room?.room_type?.name || "Room Type"}</span>
282
+ </div>
283
+ <div className="feature-item">
284
+ <Bed className="icon" size={18} />
285
+ <span>{room?.bed_type?.name || "Bed Type"}</span>
286
+ </div>
287
+ <div className="feature-item">
288
+ <Expand className="icon" size={18} />
289
+ <span>{room?.size} sq ft</span>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ <div className="rooms-design">
294
+ <div className="section-header">
295
+ <div className="sub-heading">
296
+ Features & Amenities
297
+ </div>
298
+ {showViewAllButton && (
299
+ <div className="view-all-item">
300
+ <button
301
+ onClick={() => openAmenitiesModal(room)}
302
+ className="btn-view-more"
303
+ >
304
+ <span>View All ({totalItems})</span>
305
+ </button>
306
+ </div>
307
+ )}
308
+ </div>
309
+ {displayFeatures.length > 0 ||
310
+ displayAmenities.length > 0 ? (
311
+ <div className="amenities-style">
312
+ {displayFeatures.map((feature) => {
313
+ const IconComponent = feature?.icon_name || null;
314
+ return (
315
+ <div
316
+ key={feature?.id}
317
+ className="amenity-item-style"
318
+ >
319
+ {IconComponent && <IconComponent />}
320
+ <span>{feature?.name}</span>
321
+ </div>
322
+ );
323
+ })}
324
+ {displayAmenities.map((amenity) => {
325
+ const IconComponent = amenity?.icon_name || null;
326
+ return (
327
+ <div
328
+ key={amenity?.id}
329
+ className="amenity-item-style"
330
+ >
331
+ {amenity?.image ? (
332
+ <NcImage
333
+ src={amenity?.image}
334
+ alt={amenity?.name}
335
+ className="w-8 h-8 object-cover rounded-md"
336
+ />
337
+ ) : (
338
+ IconComponent && <IconComponent />
339
+ )}
340
+ <span>{amenity?.name}</span>
341
+ </div>
342
+ );
343
+ })}
344
+ </div>
345
+ ) : (
346
+ <p
347
+ style={{
348
+ fontSize: "14px",
349
+ color: "#6b7280", // a soft gray
350
+ textAlign: "center",
351
+ }}
352
+ >
353
+ Features & Amenities not available.
354
+ </p>
355
+ )}
356
+ </div>
357
+
358
+ <AmenitiesModal
359
+ isOpen={isAmenitiesModalOpen}
360
+ onClose={closeAmenitiesModal}
361
+ room={selectedRoomForAmenities}
362
+ />
363
+ <div className="rooms-design">
364
+ <div className="section-header">
365
+ <div className="sub-heading">Room Description</div>
366
+ </div>
367
+
368
+ {room?.description?.length > 180 ? (
369
+ <div className="description-container-listing">
370
+ <span className="description">
371
+ {room?.description?.substring(0, 180)}...
372
+ </span>
373
+ <button
374
+ onClick={() => setSelectedRoomForDesc(room)}
375
+ className="btn-view-more-listing"
376
+ >
377
+ View Full Description
378
+ </button>
379
+ </div>
380
+ ) : (
381
+ <span className="description">
382
+ {room?.description}
383
+ </span>
384
+ )}
385
+ </div>
386
+
387
+ <div
388
+ style={{
389
+ width: "100%",
390
+ display: "flex",
391
+ justifyContent: "end",
392
+ alignItems: "center",
393
+ }}
394
+ >
395
+ <div
396
+ className="button-group"
397
+ style={{
398
+ width: "130px",
399
+ }}
400
+ >
401
+ <button
402
+ className={`btn-booking ${
403
+ room?.status === "inactive" ||
404
+ room?.to_book === false
405
+ ? "disabled"
406
+ : ""
407
+ }`}
408
+ onClick={() => openBookingModal(room)}
409
+ disabled={
410
+ room?.status === "inactive" ||
411
+ room?.to_book === false
412
+ }
413
+ >
414
+ <span className="price-text">
415
+ {`${currencySymbol()} ${
416
+ room?.price_per_month || 0
417
+ }`}
418
+ <span className="per-month">/mo</span>
419
+ </span>
420
+ <span className="cta-text">Book Now</span>
421
+ </button>
422
+ </div>
423
+ </div>
424
+ <RoomDescriptionModal
425
+ isOpen={!!selectedRoomForDesc}
426
+ onClose={() => setSelectedRoomForDesc(null)}
427
+ room={selectedRoomForDesc}
428
+ />
429
+ </div>
430
+ </div>
431
+ </header>
432
+ </div>
433
+
434
+ <div
435
+ className={`sidebar-container ${
436
+ propertyData?.is_shared_property ? "shared-property" : ""
437
+ }`}
438
+ >
439
+ <div>
440
+ {room?.status === "inactive" && (
441
+ <Badge name="Room is Inactive" />
442
+ )}
443
+ </div>
444
+ </div>
445
+ </div>
446
+ );
447
+ })}
448
+
449
+ <BookingModal
450
+ isOpen={isBookingModalOpen}
451
+ onClose={closeBookingModal}
452
+ room={selectedRoomForBooking}
453
+ propertyData={propertyData}
454
+ onSubmit={handleSubmit}
455
+ isLoading={isLoading}
456
+ />
457
+ </>
458
+ );
459
+ };
460
+
461
+ return (
462
+ <div className="container listing-detail-page">
463
+ <header className="listing-header" onClick={handleOpenModalImageGallery}>
464
+ <div className="image-grid">
465
+ <div className="main-image">
466
+ <LazyLoadImage
467
+ className="image-all"
468
+ src={images?.[0]?.url || ""}
469
+ alt="Property"
470
+ />
471
+ </div>
472
+
473
+ {images?.slice(1, 5)?.map((item, index) => (
474
+ <div className="grid-image" key={item?.id}>
475
+ <LazyLoadImage
476
+ className="image-grid"
477
+ src={item?.url || ""}
478
+ alt={`Thumbnail ${index + 1}`}
479
+ />
480
+ </div>
481
+ ))}
482
+
483
+ {images?.length >= 5 ? (
484
+ <div className="overlay-count">Show all ({images?.length})</div>
485
+ ) : (
486
+ <button
487
+ className="show-all-btn"
488
+ onClick={handleOpenModalImageGallery}
489
+ >
490
+ <Squares2X2Icon className="icon" />
491
+ <span>Show all ({images?.length})</span>
492
+ </button>
493
+ )}
494
+ </div>
495
+ </header>
496
+
497
+ <section className="similar-properties-section">
498
+ {/* <RoomSection propertyData={propertyData} /> */}
499
+ <RenderSection />
500
+ <PropertyDetailsCard propertyData={propertyData} />
501
+ </section>
502
+
503
+ {imageModal && (
504
+ <ListingImageGallery images={images} onClose={closeImageModal} />
505
+ )}
506
+ </div>
507
+ );
508
+ };
509
+
510
+ export default function ListingStayDetailPage() {
511
+ return <StayDetailPageContainer />;
512
+ }
@@ -0,0 +1,76 @@
1
+ import React, { useState } from "react";
2
+ import "../../styles/property-description.scss";
3
+ import ButtonPrimary from "../../shared/Button/ButtonPrimary";
4
+
5
+ const PropertyDescription = ({ propertyData }) => {
6
+ const [expandedRoomIds, setExpandedRoomIds] = useState([]);
7
+
8
+ if (!propertyData || !propertyData.rooms || propertyData.rooms.length === 0) {
9
+ return (
10
+ <div className="property-description__wrap">
11
+ No room descriptions available.
12
+ </div>
13
+ );
14
+ }
15
+
16
+ const toggleRoomDescription = (roomId) => {
17
+ if (expandedRoomIds.includes(roomId)) {
18
+ setExpandedRoomIds(expandedRoomIds.filter((id) => id !== roomId));
19
+ } else {
20
+ setExpandedRoomIds([...expandedRoomIds, roomId]);
21
+ }
22
+ };
23
+
24
+ return (
25
+ <div className="description__wrap">
26
+ <div className="description__divider"></div>
27
+
28
+ <h2 className="description__title">Room Descriptions</h2>
29
+
30
+ <div className="description__content">
31
+ {propertyData.rooms.map((room) => {
32
+ const isExpanded = expandedRoomIds.includes(room.id);
33
+ const truncatedDescription =
34
+ room.description?.length > 150
35
+ ? room.description.substring(0, 150) + "..."
36
+ : room.description || "";
37
+
38
+ return (
39
+ <div key={room.id} className="room__details">
40
+ <p className="room__description">
41
+ {isExpanded ? room.description : truncatedDescription}
42
+ </p>
43
+
44
+ {room.description?.length > 150 && (
45
+ <div className="description__button-wrap">
46
+ <ButtonPrimary onClick={() => toggleRoomDescription(room.id)}>
47
+ {isExpanded ? "Show Less" : "Show More"}
48
+ </ButtonPrimary>
49
+ </div>
50
+ )}
51
+ </div>
52
+ );
53
+ })}
54
+ </div>
55
+
56
+ <div className="description__divider" />
57
+
58
+ <div>
59
+ <h4 className="description__subtitle">Property Policies & Rules</h4>
60
+ <ul className="policy__rules-list">
61
+ {propertyData?.additional_rules?.length > 0 ? (
62
+ propertyData.additional_rules.map((rule) => (
63
+ <li key={rule.id} className="policy__rule">
64
+ {rule.name}
65
+ </li>
66
+ ))
67
+ ) : (
68
+ <li className="policy__rule">No additional rules available.</li>
69
+ )}
70
+ </ul>
71
+ </div>
72
+ </div>
73
+ );
74
+ };
75
+
76
+ export default PropertyDescription;
@@ -0,0 +1,62 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import "../../styles/propertydetailscard.scss";
3
+ import { useDispatch } from "react-redux";
4
+ import { similar_Properties } from "../../redux/slices/PropertySlice/PropertySlice";
5
+ import StayCard3 from "../StayCard/StayCard3";
6
+
7
+ const PropertyDetailsCard = ({ propertyData }) => {
8
+ const dispatch = useDispatch();
9
+ const [similarProperties, setSimilarProperties] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+ const [error, setError] = useState(null);
12
+
13
+ const property_type_id = propertyData?.property_type?.id;
14
+ const property_category_id = propertyData?.property_category?.id;
15
+ const property_id = propertyData?.id;
16
+
17
+ const fetchSimilarProperties = async () => {
18
+ setLoading(true);
19
+ setError(null);
20
+ try {
21
+ const response = await dispatch(
22
+ similar_Properties({
23
+ property_type_id: property_type_id ?? 0,
24
+ property_category_id: property_category_id ?? 0,
25
+ property_id: property_id ?? 0,
26
+ })
27
+ ).unwrap();
28
+ setSimilarProperties(response?.properties || []);
29
+ } catch (err) {
30
+ setError(err.message || "Failed to fetch similar properties");
31
+ } finally {
32
+ setLoading(false);
33
+ }
34
+ };
35
+
36
+ useEffect(() => {
37
+ if (
38
+ propertyData?.property_type?.id &&
39
+ propertyData?.property_category?.id &&
40
+ propertyData?.id
41
+ ) {
42
+ fetchSimilarProperties();
43
+ }
44
+ }, [propertyData]);
45
+
46
+ return (
47
+ <div className="my-booking-container">
48
+ <div className="my-booking-content">
49
+ <h1 className="heading">Similar Property</h1>
50
+ <div className="stay-cards">
51
+ {similarProperties?.length > 0 ? (
52
+ similarProperties?.map((room) => <StayCard3 booking={room} />)
53
+ ) : (
54
+ <p className="no-results">No Similar Property Found.</p>
55
+ )}
56
+ </div>
57
+ </div>
58
+ </div>
59
+ );
60
+ };
61
+
62
+ export default PropertyDetailsCard;