stay_commerce-frontend 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/app/assets/builds/bundle.css +8665 -6069
- data/app/assets/builds/bundle.css.map +3 -3
- data/app/assets/builds/bundle.js +37877 -27557
- data/app/assets/builds/bundle.js.map +4 -4
- data/app/assets/builds/styles.css +6328 -5031
- data/app/assets/builds/styles.css.map +3 -3
- data/app/javascript/react/components/Accountpage/AccountInfo.jsx +2 -1
- data/app/javascript/react/components/AddNewProperty/CommonLayout.jsx +0 -2
- data/app/javascript/react/components/AddNewProperty/Description.jsx +51 -52
- data/app/javascript/react/components/AddNewProperty/Details.jsx +212 -129
- data/app/javascript/react/components/AddNewProperty/Images.jsx +122 -45
- data/app/javascript/react/components/AddNewProperty/Location.jsx +21 -14
- data/app/javascript/react/components/AddNewProperty/Room.jsx +639 -548
- data/app/javascript/react/components/AvatarDropdown/AvatarDropDown.jsx +9 -1
- data/app/javascript/react/components/FacilitiesSection/Facilities.jsx +18 -0
- data/app/javascript/react/components/FixedNavbar/FixedNav.jsx +20 -14
- data/app/javascript/react/components/HeroSectionDesign/BookingForm.jsx +136 -88
- data/app/javascript/react/components/HeroSectionDesign/MyPropertiesListing.jsx +79 -69
- data/app/javascript/react/components/Layout/Layout.js +8 -1
- data/app/javascript/react/components/Listing-stay-Detail/ApartmentCard.jsx +3 -3
- data/app/javascript/react/components/Listing-stay-Detail/BookingModal.jsx +167 -122
- data/app/javascript/react/components/Listing-stay-Detail/CardManager.jsx +285 -0
- data/app/javascript/react/components/Listing-stay-Detail/CheckoutForm.jsx +147 -84
- data/app/javascript/react/components/Listing-stay-Detail/ListingStayDetailPage.jsx +1 -7
- data/app/javascript/react/components/Listing-stay-Detail/PropertiesPage.jsx +464 -0
- data/app/javascript/react/components/MobileNav/MobileMenu.jsx +1 -4
- data/app/javascript/react/components/PropertyListing/MyProperties.jsx +45 -44
- data/app/javascript/react/components/PropertyListing/StayBooking/BookingDetails.jsx +4 -4
- data/app/javascript/react/components/PropertyListing/StayBooking/MyBooking.jsx +41 -29
- data/app/javascript/react/components/StayCard/StayCard.jsx +5 -3
- data/app/javascript/react/packs/index.jsx +1 -0
- data/app/javascript/react/packs/routes/Route.jsx +18 -1
- data/app/javascript/react/pages/Home.jsx +6 -4
- data/app/javascript/react/redux/slices/PropertySlice/PropertySlice.jsx +21 -21
- data/app/javascript/react/redux/slices/PropertySlice/Searchslice.jsx +53 -6
- data/app/javascript/react/redux/slices/UserSlice/UserSlice.jsx +1 -0
- data/app/javascript/react/shared/Avatar/Avatar.jsx +5 -8
- data/app/javascript/react/shared/Button/SecondryButton.jsx +9 -0
- data/app/javascript/react/shared/Loader.jsx +13 -0
- data/app/javascript/react/shared/Pagination.jsx +53 -0
- data/app/javascript/react/shared/Schema/FormSchema +143 -0
- data/app/javascript/react/styles/BookingDetails.scss +1 -0
- data/app/javascript/react/styles/CardManager.scss +608 -0
- data/app/javascript/react/styles/Loader.scss +30 -0
- data/app/javascript/react/styles/Pagination.scss +33 -0
- data/app/javascript/react/styles/PropertiesPage.scss +0 -4
- data/app/javascript/react/styles/RenderSection.scss +1 -0
- data/app/javascript/react/styles/accountpage.scss +3 -0
- data/app/javascript/react/styles/application.scss +13 -1
- data/app/javascript/react/styles/bookingform.scss +56 -28
- data/app/javascript/react/styles/buttonSecondry.scss +24 -0
- data/app/javascript/react/styles/checkbox.scss +34 -35
- data/app/javascript/react/styles/commonlayout.scss +7 -2
- data/app/javascript/react/styles/commonpage.scss +5 -1
- data/app/javascript/react/styles/description.scss +3 -0
- data/app/javascript/react/styles/facilities.scss +2 -1
- data/app/javascript/react/styles/fixednavbar.scss +8 -0
- data/app/javascript/react/styles/listingstaydetailpage.scss +5 -0
- data/app/javascript/react/styles/mobilemenu.scss +0 -1
- data/app/javascript/react/styles/mybooking.scss +20 -0
- data/app/javascript/react/styles/myproperty.scss +26 -0
- data/app/javascript/react/styles/propertydetailscard.scss +265 -267
- data/app/javascript/react/styles/react-datepicker/react-datepicker.css +869 -0
- data/app/javascript/react/styles/room.scss +13 -8
- data/app/javascript/react/utils/helpers/ToastErros.js +12 -0
- data/db/migrate/20250627101451_add_role_to_stay_users.rb +5 -0
- data/lib/stay_commerce/frontend/version.rb +1 -1
- metadata +15 -5
- data/app/javascript/react/components/HeroSectionDesign/PropertiesPage.jsx +0 -122
- data/app/javascript/react/shared/DateField/CustomDatePicker.jsx +0 -69
- data/app/javascript/react/styles/customdatepicker.scss +0 -120
@@ -0,0 +1,464 @@
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
2
|
+
import { useFormik } from "formik";
|
3
|
+
import "../../styles/listingstaydetailpage.scss";
|
4
|
+
import moment from "moment";
|
5
|
+
import { reserveBooking } from "../../redux/slices/PropertySlice/PropertySlice";
|
6
|
+
import { useDispatch, useSelector } from "react-redux";
|
7
|
+
import GallerySlider from "../GallerySlider/GallerySlider";
|
8
|
+
import Badge from "../../shared/Badge/Badge";
|
9
|
+
import { currencySymbol } from "../../shared/CurrencySymbol";
|
10
|
+
import "../../styles/RenderSection.scss";
|
11
|
+
import { useNavigate } from "react-router-dom";
|
12
|
+
import { User, DoorOpen, Bed, Expand } from "lucide-react";
|
13
|
+
import BookingModal from "./BookingModal";
|
14
|
+
import AmenitiesModal from "./AmenitiesModal";
|
15
|
+
import RoomDescriptionModal from "./RoomDescriptionModal";
|
16
|
+
import { searchProperties } from "../../redux/slices/PropertySlice/Searchslice";
|
17
|
+
import Pagination from "../../shared/Pagination";
|
18
|
+
import Loader from "../../shared/Loader";
|
19
|
+
|
20
|
+
const PropertiesPage = () => {
|
21
|
+
const dispatch = useDispatch();
|
22
|
+
const {
|
23
|
+
searchResults,
|
24
|
+
startDate,
|
25
|
+
endDate,
|
26
|
+
totalGuests,
|
27
|
+
latitude,
|
28
|
+
longitude,
|
29
|
+
data,
|
30
|
+
loading,
|
31
|
+
} = useSelector((state) => state.search);
|
32
|
+
const roomsData = searchResults || [];
|
33
|
+
const [isBookingModalOpen, setIsBookingModalOpen] = useState(false);
|
34
|
+
const [selectedRoomForBooking, setSelectedRoomForBooking] = useState(null);
|
35
|
+
const [isAmenitiesModalOpen, setIsAmenitiesModalOpen] = useState(false);
|
36
|
+
const [selectedRoomForAmenities, setSelectedRoomForAmenities] =
|
37
|
+
useState(null);
|
38
|
+
const [currentPage, setCurrentPage] = useState(1);
|
39
|
+
|
40
|
+
useEffect(() => {
|
41
|
+
const start = startDate
|
42
|
+
? moment(startDate).format("YYYY-MM-DD")
|
43
|
+
: moment().format("YYYY-MM-DD");
|
44
|
+
const end = endDate
|
45
|
+
? moment(endDate).format("YYYY-MM-DD")
|
46
|
+
: moment().add(2, "days").format("YYYY-MM-DD");
|
47
|
+
const guests = totalGuests || 1;
|
48
|
+
const lat = latitude || "28.6139";
|
49
|
+
const lng = longitude || "77.2090";
|
50
|
+
const searchQuery = `q[availability_start_lteq]=${start}&q[availability_end_gteq]=${end}&q[guest_number_gteq]=${guests}&q[latitude]=${lat}&q[longitude]=${lng}&page=${currentPage}`;
|
51
|
+
dispatch(searchProperties(searchQuery));
|
52
|
+
}, [
|
53
|
+
dispatch,
|
54
|
+
startDate,
|
55
|
+
endDate,
|
56
|
+
totalGuests,
|
57
|
+
latitude,
|
58
|
+
longitude,
|
59
|
+
currentPage,
|
60
|
+
]);
|
61
|
+
|
62
|
+
const RenderSection = () => {
|
63
|
+
const navigate = useNavigate();
|
64
|
+
const [error, setError] = useState("");
|
65
|
+
const [isButtonDisabled, setIsButtonDisabled] = useState(true);
|
66
|
+
const [isLoading, setIsLoading] = useState(false);
|
67
|
+
const dispatch = useDispatch();
|
68
|
+
const [selectedRoomForDesc, setSelectedRoomForDesc] = useState(null);
|
69
|
+
|
70
|
+
const formik = useFormik({
|
71
|
+
initialValues: {
|
72
|
+
number_of_guests: null,
|
73
|
+
booking_start: null,
|
74
|
+
booking_end: null,
|
75
|
+
room_id: null,
|
76
|
+
line_items_attributes: [],
|
77
|
+
price_per_month: null,
|
78
|
+
},
|
79
|
+
enableReinitialize: true,
|
80
|
+
onSubmit: (values) => {
|
81
|
+
handleSubmit(values);
|
82
|
+
},
|
83
|
+
});
|
84
|
+
|
85
|
+
const calculateMonthsDifference = (bookingStart, bookingEnd) => {
|
86
|
+
const start = moment(bookingStart, "YYYY-MM-DD");
|
87
|
+
const end = moment(bookingEnd, "YYYY-MM-DD");
|
88
|
+
const uniqueMonths = new Set();
|
89
|
+
let current = start.clone();
|
90
|
+
while (current.isBefore(end, "day") || current.isSame(end, "day")) {
|
91
|
+
uniqueMonths.add(`${current.year()}-${current.month() + 1}`);
|
92
|
+
current.add(1, "day");
|
93
|
+
}
|
94
|
+
return uniqueMonths.size;
|
95
|
+
};
|
96
|
+
|
97
|
+
const handleSubmit = async (values) => {
|
98
|
+
const propertyId = selectedRoomForBooking?.property?.slug;
|
99
|
+
if (!propertyId) {
|
100
|
+
setError("Property ID not found. Please try again.");
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
|
104
|
+
const postData = {
|
105
|
+
booking: {
|
106
|
+
number_of_guests: Number(values.number_of_guests),
|
107
|
+
property_id: propertyId,
|
108
|
+
check_in_date: moment(values.booking_start).format("YYYY-MM-DD"),
|
109
|
+
check_out_date: moment(values.booking_end).format("YYYY-MM-DD"),
|
110
|
+
no_of_months: calculateMonthsDifference(
|
111
|
+
moment(values.booking_start).format("YYYY-MM-DD"),
|
112
|
+
moment(values.booking_end).format("YYYY-MM-DD")
|
113
|
+
),
|
114
|
+
line_items_attributes: [
|
115
|
+
{
|
116
|
+
room_id: values.room_id,
|
117
|
+
price: values.price_per_month,
|
118
|
+
quantity: 1,
|
119
|
+
property_id: propertyId,
|
120
|
+
},
|
121
|
+
],
|
122
|
+
},
|
123
|
+
};
|
124
|
+
setIsLoading(true);
|
125
|
+
try {
|
126
|
+
const action = await dispatch(reserveBooking(postData));
|
127
|
+
|
128
|
+
if (action?.type === "payment/reserveBooking/fulfilled" && action) {
|
129
|
+
const bookingId = action?.payload?.id;
|
130
|
+
if (bookingId) {
|
131
|
+
setError("");
|
132
|
+
setIsBookingModalOpen(false);
|
133
|
+
navigate(`/myreservations/${bookingId}`);
|
134
|
+
} else {
|
135
|
+
setError("Failed to reserve booking. Please try again.");
|
136
|
+
}
|
137
|
+
} else {
|
138
|
+
setError(
|
139
|
+
action?.payload?.message ||
|
140
|
+
"Failed to reserve booking. Please try again."
|
141
|
+
);
|
142
|
+
}
|
143
|
+
} catch (error) {
|
144
|
+
console.error("Booking Error:", error);
|
145
|
+
setError("Failed to reserve booking. Please try again.");
|
146
|
+
} finally {
|
147
|
+
setIsLoading(false);
|
148
|
+
}
|
149
|
+
};
|
150
|
+
|
151
|
+
const openBookingModal = (room) => {
|
152
|
+
setSelectedRoomForBooking(room);
|
153
|
+
setIsBookingModalOpen(true);
|
154
|
+
};
|
155
|
+
|
156
|
+
const closeBookingModal = () => {
|
157
|
+
setIsBookingModalOpen(false);
|
158
|
+
setSelectedRoomForBooking(null);
|
159
|
+
setError("");
|
160
|
+
};
|
161
|
+
|
162
|
+
const openAmenitiesModal = (room) => {
|
163
|
+
setSelectedRoomForAmenities(room);
|
164
|
+
setIsAmenitiesModalOpen(true);
|
165
|
+
};
|
166
|
+
|
167
|
+
const closeAmenitiesModal = () => {
|
168
|
+
setIsAmenitiesModalOpen(false);
|
169
|
+
setSelectedRoomForAmenities(null);
|
170
|
+
};
|
171
|
+
|
172
|
+
const modifiedStatus = (status) => {
|
173
|
+
switch (status) {
|
174
|
+
case "active":
|
175
|
+
return "Active";
|
176
|
+
case "inactive":
|
177
|
+
return "Inactive";
|
178
|
+
default:
|
179
|
+
return "";
|
180
|
+
}
|
181
|
+
};
|
182
|
+
|
183
|
+
useEffect(() => {
|
184
|
+
const { booking_start, booking_end, number_of_guests } = formik.values;
|
185
|
+
if (booking_start && booking_end && number_of_guests) {
|
186
|
+
setIsButtonDisabled(false);
|
187
|
+
} else {
|
188
|
+
setIsButtonDisabled(true);
|
189
|
+
}
|
190
|
+
}, [formik.values]);
|
191
|
+
|
192
|
+
const selectedRoom = formik?.values?.room_id;
|
193
|
+
if (!roomsData || roomsData.length === 0) {
|
194
|
+
return (
|
195
|
+
<div className="no-results">
|
196
|
+
<p>No rooms available.</p>
|
197
|
+
</div>
|
198
|
+
);
|
199
|
+
}
|
200
|
+
|
201
|
+
return (
|
202
|
+
<>
|
203
|
+
<h2 className="section-title">Rooms</h2>
|
204
|
+
{roomsData.map((room, index) => {
|
205
|
+
const features = room?.features || [];
|
206
|
+
const amenities = room?.amenities || [];
|
207
|
+
const totalItems = features.length + amenities.length;
|
208
|
+
const showViewAllButton = totalItems > 4;
|
209
|
+
const displayFeatures = showViewAllButton
|
210
|
+
? features.slice(0, 2)
|
211
|
+
: features;
|
212
|
+
const displayAmenities = showViewAllButton
|
213
|
+
? amenities.slice(0, 2)
|
214
|
+
: amenities;
|
215
|
+
|
216
|
+
return (
|
217
|
+
<div key={room?.id} className="section-container">
|
218
|
+
<div className="room-container">
|
219
|
+
<header>
|
220
|
+
<div className="room-item-wrapper">
|
221
|
+
<div
|
222
|
+
className="room-image"
|
223
|
+
style={{ position: "relative" }}
|
224
|
+
>
|
225
|
+
<GallerySlider
|
226
|
+
ratioClass="gallery-aspect"
|
227
|
+
images={room?.room_images || []}
|
228
|
+
style={{
|
229
|
+
height: "370px",
|
230
|
+
borderTopLeftRadius: "20px",
|
231
|
+
borderBottomLeftRadius: "20px",
|
232
|
+
}}
|
233
|
+
/>
|
234
|
+
<div
|
235
|
+
style={{
|
236
|
+
position: "absolute",
|
237
|
+
top: "12px",
|
238
|
+
left: "12px",
|
239
|
+
zIndex: 10,
|
240
|
+
}}
|
241
|
+
>
|
242
|
+
<Badge
|
243
|
+
name={modifiedStatus(room?.status) || "Property"}
|
244
|
+
/>
|
245
|
+
</div>
|
246
|
+
</div>
|
247
|
+
|
248
|
+
<div style={{ width: "100%", padding: "20px 20px 0 0" }}>
|
249
|
+
<div className="room-content">
|
250
|
+
<div className="rooms-parents">
|
251
|
+
<div
|
252
|
+
style={{ display: "flex", alignItems: "center" }}
|
253
|
+
>
|
254
|
+
<h2 className="room-title">{room?.name}</h2>
|
255
|
+
</div>
|
256
|
+
</div>
|
257
|
+
<hr style={{ marginTop: 0 }} />
|
258
|
+
|
259
|
+
<div className="room-features">
|
260
|
+
<div className="feature-item">
|
261
|
+
<User className="icon" size={18} />
|
262
|
+
<span>{room?.max_guests || "N/A"}</span>
|
263
|
+
</div>
|
264
|
+
<div className="feature-item">
|
265
|
+
<DoorOpen className="icon" size={18} />
|
266
|
+
<span>{room?.room_type?.name || "Room Type"}</span>
|
267
|
+
</div>
|
268
|
+
<div className="feature-item">
|
269
|
+
<Bed className="icon" size={18} />
|
270
|
+
<span>{room?.bed_type?.name || "Bed Type"}</span>
|
271
|
+
</div>
|
272
|
+
<div className="feature-item">
|
273
|
+
<Expand className="icon" size={18} />
|
274
|
+
<span>
|
275
|
+
{room?.size ? `${room.size} sq ft` : "Size N/A"}
|
276
|
+
</span>
|
277
|
+
</div>
|
278
|
+
</div>
|
279
|
+
</div>
|
280
|
+
|
281
|
+
<div className="rooms-design">
|
282
|
+
<div className="section-header">
|
283
|
+
<div className="sub-heading">
|
284
|
+
Features & Amenities
|
285
|
+
</div>
|
286
|
+
{showViewAllButton && (
|
287
|
+
<div className="view-all-item">
|
288
|
+
<button
|
289
|
+
onClick={() => openAmenitiesModal(room)}
|
290
|
+
className="btn-view-more"
|
291
|
+
>
|
292
|
+
<span>View All ({totalItems})</span>
|
293
|
+
</button>
|
294
|
+
</div>
|
295
|
+
)}
|
296
|
+
</div>
|
297
|
+
{displayFeatures.length > 0 ||
|
298
|
+
displayAmenities.length > 0 ? (
|
299
|
+
<div className="amenities-style">
|
300
|
+
{displayFeatures.map((feature) => {
|
301
|
+
const IconComponent = feature?.icon_name || null;
|
302
|
+
return (
|
303
|
+
<div
|
304
|
+
key={feature?.id}
|
305
|
+
className="amenity-item-style"
|
306
|
+
>
|
307
|
+
{IconComponent && <IconComponent />}
|
308
|
+
<span>{feature?.name}</span>
|
309
|
+
</div>
|
310
|
+
);
|
311
|
+
})}
|
312
|
+
{displayAmenities.map((amenity) => {
|
313
|
+
const IconComponent = amenity?.icon_name || null;
|
314
|
+
return (
|
315
|
+
<div
|
316
|
+
key={amenity?.id}
|
317
|
+
className="amenity-item-style"
|
318
|
+
>
|
319
|
+
{amenity?.image ? (
|
320
|
+
<NcImage
|
321
|
+
src={amenity?.image}
|
322
|
+
alt={amenity?.name}
|
323
|
+
className="w-8 h-8 object-cover rounded-md"
|
324
|
+
/>
|
325
|
+
) : (
|
326
|
+
IconComponent && <IconComponent />
|
327
|
+
)}
|
328
|
+
<span>{amenity?.name}</span>
|
329
|
+
</div>
|
330
|
+
);
|
331
|
+
})}
|
332
|
+
</div>
|
333
|
+
) : (
|
334
|
+
<p
|
335
|
+
style={{
|
336
|
+
fontSize: "14px",
|
337
|
+
color: "#6b7280",
|
338
|
+
textAlign: "center",
|
339
|
+
}}
|
340
|
+
>
|
341
|
+
Features & Amenities not available.
|
342
|
+
</p>
|
343
|
+
)}
|
344
|
+
</div>
|
345
|
+
|
346
|
+
<AmenitiesModal
|
347
|
+
isOpen={isAmenitiesModalOpen}
|
348
|
+
onClose={closeAmenitiesModal}
|
349
|
+
room={selectedRoomForAmenities}
|
350
|
+
/>
|
351
|
+
|
352
|
+
<div className="rooms-design">
|
353
|
+
<div className="section-header">
|
354
|
+
<div className="sub-heading">Room Description</div>
|
355
|
+
</div>
|
356
|
+
|
357
|
+
{room?.description?.length > 180 ? (
|
358
|
+
<div className="description-container-listing">
|
359
|
+
<span className="description">
|
360
|
+
{room?.description?.substring(0, 180)}...
|
361
|
+
</span>
|
362
|
+
<button
|
363
|
+
onClick={() => setSelectedRoomForDesc(room)}
|
364
|
+
className="btn-view-more-listing"
|
365
|
+
>
|
366
|
+
View Full Description
|
367
|
+
</button>
|
368
|
+
</div>
|
369
|
+
) : (
|
370
|
+
<span className="description">
|
371
|
+
{room?.description || "No description available."}
|
372
|
+
</span>
|
373
|
+
)}
|
374
|
+
</div>
|
375
|
+
|
376
|
+
<div
|
377
|
+
style={{
|
378
|
+
width: "100%",
|
379
|
+
display: "flex",
|
380
|
+
justifyContent: "end",
|
381
|
+
alignItems: "center",
|
382
|
+
}}
|
383
|
+
>
|
384
|
+
<div
|
385
|
+
className="button-group"
|
386
|
+
style={{
|
387
|
+
width: "130px",
|
388
|
+
}}
|
389
|
+
>
|
390
|
+
<button
|
391
|
+
className={`btn-booking ${
|
392
|
+
room?.status === "inactive" ? "disabled" : ""
|
393
|
+
}`}
|
394
|
+
onClick={() => openBookingModal(room)}
|
395
|
+
disabled={room?.status === "inactive"}
|
396
|
+
>
|
397
|
+
<span className="price-text">
|
398
|
+
{`${currencySymbol()}${
|
399
|
+
room?.price_per_month || 0
|
400
|
+
}`}
|
401
|
+
<span className="per-month">/mo</span>
|
402
|
+
</span>
|
403
|
+
<span className="cta-text">Book Now</span>
|
404
|
+
</button>
|
405
|
+
</div>
|
406
|
+
</div>
|
407
|
+
|
408
|
+
<RoomDescriptionModal
|
409
|
+
isOpen={!!selectedRoomForDesc}
|
410
|
+
onClose={() => setSelectedRoomForDesc(null)}
|
411
|
+
room={selectedRoomForDesc}
|
412
|
+
/>
|
413
|
+
</div>
|
414
|
+
</div>
|
415
|
+
</header>
|
416
|
+
</div>
|
417
|
+
|
418
|
+
<div className="sidebar-container">
|
419
|
+
<div>
|
420
|
+
{room?.status === "inactive" && (
|
421
|
+
<Badge name="Room is Inactive" />
|
422
|
+
)}
|
423
|
+
</div>
|
424
|
+
</div>
|
425
|
+
</div>
|
426
|
+
);
|
427
|
+
})}
|
428
|
+
|
429
|
+
<BookingModal
|
430
|
+
isOpen={isBookingModalOpen}
|
431
|
+
onClose={closeBookingModal}
|
432
|
+
room={selectedRoomForBooking}
|
433
|
+
propertyData={selectedRoomForBooking?.property}
|
434
|
+
onSubmit={handleSubmit}
|
435
|
+
isLoading={isLoading}
|
436
|
+
/>
|
437
|
+
</>
|
438
|
+
);
|
439
|
+
};
|
440
|
+
return (
|
441
|
+
<div className="container listing-detail-page">
|
442
|
+
<section className="similar-properties-section">
|
443
|
+
{loading || !data?.success ? (
|
444
|
+
<Loader text="Searching Properties..." />
|
445
|
+
) : (
|
446
|
+
<>
|
447
|
+
<RenderSection />
|
448
|
+
{data?.meta?.total_pages > 1 && (
|
449
|
+
<Pagination
|
450
|
+
currentPage={data.meta.current_page}
|
451
|
+
totalPages={data.meta.total_pages}
|
452
|
+
onPageChange={setCurrentPage}
|
453
|
+
/>
|
454
|
+
)}
|
455
|
+
</>
|
456
|
+
)}
|
457
|
+
</section>
|
458
|
+
</div>
|
459
|
+
);
|
460
|
+
};
|
461
|
+
|
462
|
+
export default function ListingStayDetailPage() {
|
463
|
+
return <PropertiesPage />;
|
464
|
+
}
|
@@ -12,11 +12,8 @@ function MobileMenu() {
|
|
12
12
|
const navItems = [
|
13
13
|
{ name: "HOME", path: "/" },
|
14
14
|
{ name: "ABOUT US", path: "/about-us" },
|
15
|
-
{ name: "ACCOMMODATION", path: "/accommodation" },
|
15
|
+
// { name: "ACCOMMODATION", path: "/accommodation" },
|
16
16
|
{ name: "GALLERY", path: "/gallery" },
|
17
|
-
{ name: "BLOG", path: "/blog" },
|
18
|
-
{ name: "ELEMENT", path: "/element" },
|
19
|
-
{ name: "CONTACT", path: "/contact" },
|
20
17
|
];
|
21
18
|
|
22
19
|
return (
|
@@ -5,8 +5,8 @@ import PropertyCard from "../Headers/PropertyCard";
|
|
5
5
|
import ButtonPrimary from "../../shared/Button/ButtonPrimary";
|
6
6
|
import "../../styles/myproperty.scss";
|
7
7
|
import { getallPropertieslisting } from "../../redux/slices/PropertySlice/PropertySlice";
|
8
|
-
import PropertiesPage from "../HeroSectionDesign/PropertiesPage";
|
9
8
|
import MyPropertiesListing from "../HeroSectionDesign/MyPropertiesListing";
|
9
|
+
import Pagination from "../../shared/Pagination";
|
10
10
|
|
11
11
|
const MyProperties = () => {
|
12
12
|
const dispatch = useDispatch();
|
@@ -23,7 +23,7 @@ const MyProperties = () => {
|
|
23
23
|
id: apiItem?.id,
|
24
24
|
title: apiItem?.title,
|
25
25
|
place_images: apiItem?.place_images,
|
26
|
-
address: `${apiItem?.
|
26
|
+
address: `${apiItem?.address}, ${apiItem?.country}`,
|
27
27
|
availability_start: apiItem?.availability_start,
|
28
28
|
availability_end: apiItem?.availability_end,
|
29
29
|
price: apiItem?.price_per_month,
|
@@ -52,38 +52,23 @@ const MyProperties = () => {
|
|
52
52
|
setTotalPages(res?.meta?.total_pages || 1);
|
53
53
|
} else {
|
54
54
|
setAllData([]);
|
55
|
-
setTotalPages(1);
|
55
|
+
setTotalPages(1);
|
56
56
|
}
|
57
57
|
} catch (error) {
|
58
58
|
console.error("Error fetching properties:", error);
|
59
59
|
setAllData([]);
|
60
|
-
setTotalPages(1);
|
60
|
+
setTotalPages(1);
|
61
61
|
} finally {
|
62
62
|
setIsLoading(false);
|
63
63
|
}
|
64
64
|
};
|
65
65
|
|
66
|
-
// // 🔍 Search handler
|
67
|
-
// const handleSearch = async () => {
|
68
|
-
// if (!query.trim()) return;
|
69
|
-
// setIsSearchLoading(true);
|
70
|
-
// try {
|
71
|
-
// await fetchData(1);
|
72
|
-
// setPage(1);
|
73
|
-
// } catch (error) {
|
74
|
-
// console.error("Search error:", error);
|
75
|
-
// } finally {
|
76
|
-
// setIsSearchLoading(false);
|
77
|
-
// }
|
78
|
-
// };
|
79
66
|
const handleSearch = async () => {
|
80
67
|
if (!query.trim()) return;
|
81
68
|
setIsSearchLoading(true);
|
82
69
|
try {
|
83
|
-
|
84
|
-
|
85
|
-
setPage(1); // ✅ Set page only when fetch is successful
|
86
|
-
}
|
70
|
+
await fetchData(1);
|
71
|
+
setPage(1);
|
87
72
|
} catch (error) {
|
88
73
|
console.error("Search error:", error);
|
89
74
|
} finally {
|
@@ -91,15 +76,18 @@ const MyProperties = () => {
|
|
91
76
|
}
|
92
77
|
};
|
93
78
|
|
94
|
-
// ⌨️ Handle query input
|
95
79
|
const handleSearchChange = (event) => {
|
96
80
|
setQuery(event.target.value);
|
97
81
|
};
|
98
82
|
|
99
|
-
|
83
|
+
useEffect(() => {
|
84
|
+
fetchData(page);
|
85
|
+
}, [page]);
|
86
|
+
|
100
87
|
useEffect(() => {
|
101
88
|
if (query.length === 0) {
|
102
89
|
fetchData(1);
|
90
|
+
setPage(1);
|
103
91
|
}
|
104
92
|
}, [query]);
|
105
93
|
|
@@ -109,34 +97,47 @@ const MyProperties = () => {
|
|
109
97
|
<div className="myListing-header">
|
110
98
|
{/* 🔍 Search Input */}
|
111
99
|
{/* <div className="myListing-search">
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
100
|
+
<div className="search-bar">
|
101
|
+
<input
|
102
|
+
type="text"
|
103
|
+
value={query}
|
104
|
+
onChange={handleSearchChange}
|
105
|
+
placeholder="Search By Property Name..."
|
106
|
+
className="search-input"
|
107
|
+
/>
|
108
|
+
<ButtonPrimary
|
109
|
+
onClick={handleSearch}
|
110
|
+
className={`search-button ${
|
111
|
+
isSearchLoading || query.trim() === ""
|
112
|
+
? "search-button-disabled"
|
113
|
+
: "search-button-active"
|
114
|
+
}`}
|
115
|
+
disabled={isSearchLoading || query.trim() === ""}
|
116
|
+
>
|
117
|
+
{isSearchLoading ? "Searching..." : "Search"}
|
118
|
+
</ButtonPrimary>
|
119
|
+
</div>
|
120
|
+
</div> */}
|
133
121
|
<div className="myListing-property-cards">
|
134
122
|
<MyPropertiesListing
|
135
123
|
heading="My Properties"
|
136
124
|
stayListings={allData}
|
137
125
|
setAllData={setAllData}
|
126
|
+
isLoading={isLoading}
|
138
127
|
/>
|
139
128
|
</div>
|
129
|
+
{totalPages > 1 && (
|
130
|
+
<div className="myListing-pagination">
|
131
|
+
<Pagination
|
132
|
+
currentPage={page}
|
133
|
+
totalPages={totalPages}
|
134
|
+
onPageChange={(newPage) => {
|
135
|
+
if (newPage !== page) setPage(newPage);
|
136
|
+
}}
|
137
|
+
isLoading={isLoading}
|
138
|
+
/>
|
139
|
+
</div>
|
140
|
+
)}
|
140
141
|
</div>
|
141
142
|
</div>
|
142
143
|
</div>
|
@@ -89,13 +89,13 @@ const BookingInterface = () => {
|
|
89
89
|
|
90
90
|
<div className="property-card">
|
91
91
|
<GallerySlider
|
92
|
-
images={bookingRequestDetails?.
|
92
|
+
images={bookingRequestDetails?.rooms[0]?.room_images}
|
93
93
|
alt="Luxury Penthouse"
|
94
94
|
style={{ height: "300px" }}
|
95
95
|
/>
|
96
96
|
<div className="property-content">
|
97
97
|
<h3 className="property-title">
|
98
|
-
{bookingRequestDetails?.
|
98
|
+
{bookingRequestDetails?.rooms?.[0].name}
|
99
99
|
</h3>
|
100
100
|
<div className="property-features">
|
101
101
|
<div className="guest-badge">
|
@@ -116,7 +116,7 @@ const BookingInterface = () => {
|
|
116
116
|
<div className="price-section-details">
|
117
117
|
<div className="period">Total</div>
|
118
118
|
<div className="amount">
|
119
|
-
$
|
119
|
+
${bookingRequestDetails?.item_total}
|
120
120
|
</div>
|
121
121
|
</div>
|
122
122
|
</div>
|
@@ -142,7 +142,7 @@ const BookingInterface = () => {
|
|
142
142
|
<div className="detail-row">
|
143
143
|
<span className="label">Pay Amount:</span>
|
144
144
|
<span className="value">
|
145
|
-
$
|
145
|
+
${bookingRequestDetails?.item_total}
|
146
146
|
</span>
|
147
147
|
</div>
|
148
148
|
<div className="detail-row">
|