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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/builds/bundle.css +8665 -6069
  3. data/app/assets/builds/bundle.css.map +3 -3
  4. data/app/assets/builds/bundle.js +37877 -27557
  5. data/app/assets/builds/bundle.js.map +4 -4
  6. data/app/assets/builds/styles.css +6328 -5031
  7. data/app/assets/builds/styles.css.map +3 -3
  8. data/app/javascript/react/components/Accountpage/AccountInfo.jsx +2 -1
  9. data/app/javascript/react/components/AddNewProperty/CommonLayout.jsx +0 -2
  10. data/app/javascript/react/components/AddNewProperty/Description.jsx +51 -52
  11. data/app/javascript/react/components/AddNewProperty/Details.jsx +212 -129
  12. data/app/javascript/react/components/AddNewProperty/Images.jsx +122 -45
  13. data/app/javascript/react/components/AddNewProperty/Location.jsx +21 -14
  14. data/app/javascript/react/components/AddNewProperty/Room.jsx +639 -548
  15. data/app/javascript/react/components/AvatarDropdown/AvatarDropDown.jsx +9 -1
  16. data/app/javascript/react/components/FacilitiesSection/Facilities.jsx +18 -0
  17. data/app/javascript/react/components/FixedNavbar/FixedNav.jsx +20 -14
  18. data/app/javascript/react/components/HeroSectionDesign/BookingForm.jsx +136 -88
  19. data/app/javascript/react/components/HeroSectionDesign/MyPropertiesListing.jsx +79 -69
  20. data/app/javascript/react/components/Layout/Layout.js +8 -1
  21. data/app/javascript/react/components/Listing-stay-Detail/ApartmentCard.jsx +3 -3
  22. data/app/javascript/react/components/Listing-stay-Detail/BookingModal.jsx +167 -122
  23. data/app/javascript/react/components/Listing-stay-Detail/CardManager.jsx +285 -0
  24. data/app/javascript/react/components/Listing-stay-Detail/CheckoutForm.jsx +147 -84
  25. data/app/javascript/react/components/Listing-stay-Detail/ListingStayDetailPage.jsx +1 -7
  26. data/app/javascript/react/components/Listing-stay-Detail/PropertiesPage.jsx +464 -0
  27. data/app/javascript/react/components/MobileNav/MobileMenu.jsx +1 -4
  28. data/app/javascript/react/components/PropertyListing/MyProperties.jsx +45 -44
  29. data/app/javascript/react/components/PropertyListing/StayBooking/BookingDetails.jsx +4 -4
  30. data/app/javascript/react/components/PropertyListing/StayBooking/MyBooking.jsx +41 -29
  31. data/app/javascript/react/components/StayCard/StayCard.jsx +5 -3
  32. data/app/javascript/react/packs/index.jsx +1 -0
  33. data/app/javascript/react/packs/routes/Route.jsx +18 -1
  34. data/app/javascript/react/pages/Home.jsx +6 -4
  35. data/app/javascript/react/redux/slices/PropertySlice/PropertySlice.jsx +21 -21
  36. data/app/javascript/react/redux/slices/PropertySlice/Searchslice.jsx +53 -6
  37. data/app/javascript/react/redux/slices/UserSlice/UserSlice.jsx +1 -0
  38. data/app/javascript/react/shared/Avatar/Avatar.jsx +5 -8
  39. data/app/javascript/react/shared/Button/SecondryButton.jsx +9 -0
  40. data/app/javascript/react/shared/Loader.jsx +13 -0
  41. data/app/javascript/react/shared/Pagination.jsx +53 -0
  42. data/app/javascript/react/shared/Schema/FormSchema +143 -0
  43. data/app/javascript/react/styles/BookingDetails.scss +1 -0
  44. data/app/javascript/react/styles/CardManager.scss +608 -0
  45. data/app/javascript/react/styles/Loader.scss +30 -0
  46. data/app/javascript/react/styles/Pagination.scss +33 -0
  47. data/app/javascript/react/styles/PropertiesPage.scss +0 -4
  48. data/app/javascript/react/styles/RenderSection.scss +1 -0
  49. data/app/javascript/react/styles/accountpage.scss +3 -0
  50. data/app/javascript/react/styles/application.scss +13 -1
  51. data/app/javascript/react/styles/bookingform.scss +56 -28
  52. data/app/javascript/react/styles/buttonSecondry.scss +24 -0
  53. data/app/javascript/react/styles/checkbox.scss +34 -35
  54. data/app/javascript/react/styles/commonlayout.scss +7 -2
  55. data/app/javascript/react/styles/commonpage.scss +5 -1
  56. data/app/javascript/react/styles/description.scss +3 -0
  57. data/app/javascript/react/styles/facilities.scss +2 -1
  58. data/app/javascript/react/styles/fixednavbar.scss +8 -0
  59. data/app/javascript/react/styles/listingstaydetailpage.scss +5 -0
  60. data/app/javascript/react/styles/mobilemenu.scss +0 -1
  61. data/app/javascript/react/styles/mybooking.scss +20 -0
  62. data/app/javascript/react/styles/myproperty.scss +26 -0
  63. data/app/javascript/react/styles/propertydetailscard.scss +265 -267
  64. data/app/javascript/react/styles/react-datepicker/react-datepicker.css +869 -0
  65. data/app/javascript/react/styles/room.scss +13 -8
  66. data/app/javascript/react/utils/helpers/ToastErros.js +12 -0
  67. data/db/migrate/20250627101451_add_role_to_stay_users.rb +5 -0
  68. data/lib/stay_commerce/frontend/version.rb +1 -1
  69. metadata +15 -5
  70. data/app/javascript/react/components/HeroSectionDesign/PropertiesPage.jsx +0 -122
  71. data/app/javascript/react/shared/DateField/CustomDatePicker.jsx +0 -69
  72. data/app/javascript/react/styles/customdatepicker.scss +0 -120
@@ -5,6 +5,7 @@ import { useFormik } from "formik";
5
5
  import ButtonPrimary from "../../shared/Button/ButtonPrimary";
6
6
  import { currencySymbol } from "../../shared/CurrencySymbol";
7
7
  import Badge from "../../shared/Badge/Badge";
8
+ import DatePicker from "react-datepicker";
8
9
 
9
10
  const BookingModal = ({
10
11
  isOpen,
@@ -13,10 +14,26 @@ const BookingModal = ({
13
14
  propertyData,
14
15
  onSubmit,
15
16
  isLoading,
17
+ error: externalError,
16
18
  }) => {
17
19
  const [error, setError] = useState("");
18
20
  const [isButtonDisabled, setIsButtonDisabled] = useState(true);
21
+ const bookedDateRanges = room?.booked_dates || [];
22
+ const excludedDates = bookedDateRanges.flatMap(([start, end]) => {
23
+ const startDate = moment(start);
24
+ const endDate = moment(end);
25
+ const dates = [];
19
26
 
27
+ for (
28
+ let m = startDate.clone();
29
+ m.isSameOrBefore(endDate);
30
+ m.add(1, "day")
31
+ ) {
32
+ dates.push(m.toDate());
33
+ }
34
+
35
+ return dates;
36
+ });
20
37
  const formik = useFormik({
21
38
  initialValues: {
22
39
  number_of_guests: null,
@@ -31,6 +48,14 @@ const BookingModal = ({
31
48
  },
32
49
  });
33
50
 
51
+ // Update error state when external error changes
52
+ useEffect(() => {
53
+ if (externalError) {
54
+ setError(externalError);
55
+ }
56
+ }, [externalError]);
57
+
58
+ // Update form validation
34
59
  useEffect(() => {
35
60
  const { booking_start, booking_end, number_of_guests } = formik.values;
36
61
  if (booking_start && booking_end && number_of_guests) {
@@ -40,12 +65,44 @@ const BookingModal = ({
40
65
  }
41
66
  }, [formik.values]);
42
67
 
68
+ // Reset form when room changes
69
+ useEffect(() => {
70
+ if (room) {
71
+ formik.setValues({
72
+ number_of_guests: null,
73
+ booking_start: null,
74
+ booking_end: null,
75
+ room_id: room.id,
76
+ price_per_month: room.price_per_month,
77
+ });
78
+ setError("");
79
+ }
80
+ }, [room]);
81
+
43
82
  const handleBackdropClick = (e) => {
44
83
  if (e.target === e.currentTarget) {
45
84
  onClose();
46
85
  }
47
86
  };
48
87
 
88
+ const handleFormSubmit = (e) => {
89
+ e.preventDefault();
90
+ if (
91
+ !formik.values.booking_start ||
92
+ !formik.values.booking_end ||
93
+ !formik.values.number_of_guests
94
+ ) {
95
+ setError("Please fill in all required fields");
96
+ return;
97
+ }
98
+
99
+ // Clear any existing errors
100
+ setError("");
101
+
102
+ // Call the submit handler
103
+ formik.handleSubmit();
104
+ };
105
+
49
106
  if (!isOpen || !room) return null;
50
107
 
51
108
  return (
@@ -99,10 +156,23 @@ const BookingModal = ({
99
156
  >
100
157
  <X size={20} />
101
158
  </button>
159
+
102
160
  <div style={{ marginBottom: "24px", paddingRight: "40px" }}>
103
161
  <h2 style={{ margin: 0, fontSize: "24px", fontWeight: "600" }}>
104
162
  {room?.name}
105
163
  </h2>
164
+ {/* Display Property Information */}
165
+ {propertyData && (
166
+ <p
167
+ style={{
168
+ margin: "8px 0 0 0",
169
+ color: "#6b7280",
170
+ fontSize: "14px",
171
+ }}
172
+ >
173
+ Property: {propertyData.title}
174
+ </p>
175
+ )}
106
176
  </div>
107
177
 
108
178
  <div className="listingSectionSidebar__wrap">
@@ -133,7 +203,7 @@ const BookingModal = ({
133
203
  </span>
134
204
  </div>
135
205
 
136
- <form onSubmit={formik.handleSubmit}>
206
+ <form onSubmit={handleFormSubmit}>
137
207
  <div
138
208
  className="form-content"
139
209
  style={{ display: "flex", flexDirection: "column", gap: "10px" }}
@@ -149,21 +219,17 @@ const BookingModal = ({
149
219
  >
150
220
  Availability Start Date
151
221
  </label>
152
- <input
222
+ <DatePicker
153
223
  id={`booking_start_${room?.id}`}
154
- type="date"
155
224
  className="form-input-large"
156
- disabled={room?.to_book === false}
157
- value={
158
- formik?.values?.booking_start
159
- ? moment(formik?.values?.booking_start).format(
160
- "YYYY-MM-DD"
161
- )
162
- : ""
225
+ selected={
226
+ formik.values.booking_start
227
+ ? new Date(formik.values.booking_start)
228
+ : null
163
229
  }
164
- onChange={(e) => {
230
+ onChange={(date) => {
165
231
  setError("");
166
- formik.setFieldValue("booking_start", e.target.value);
232
+ formik.setFieldValue("booking_start", date);
167
233
  formik.setFieldValue("booking_end", null);
168
234
  formik.setFieldValue("number_of_guests", null);
169
235
  formik.setFieldValue("room_id", room?.id);
@@ -172,10 +238,11 @@ const BookingModal = ({
172
238
  room?.price_per_month
173
239
  );
174
240
  }}
175
- min={
241
+ disabled={room?.to_book === false}
242
+ minDate={
176
243
  room?.to_book === true
177
- ? typeof room?.next_available_date === "string"
178
- ? moment(room?.next_available_date).format("YYYY-MM-DD")
244
+ ? room?.next_available_date
245
+ ? new Date(room.next_available_date)
179
246
  : moment(
180
247
  moment(
181
248
  room?.booking_start ??
@@ -184,7 +251,7 @@ const BookingModal = ({
184
251
  ? room?.booking_start ??
185
252
  propertyData?.availability_start
186
253
  : new Date()
187
- ).format("YYYY-MM-DD")
254
+ ).toDate()
188
255
  : moment(
189
256
  moment(
190
257
  room?.booking_start ??
@@ -193,24 +260,19 @@ const BookingModal = ({
193
260
  ? room?.booking_start ??
194
261
  propertyData?.availability_start
195
262
  : new Date()
196
- ).format("YYYY-MM-DD")
263
+ ).toDate()
197
264
  }
198
- max={
265
+ maxDate={
199
266
  room?.booking_end
200
- ? moment(room?.booking_end).format("YYYY-MM-DD")
267
+ ? new Date(room.booking_end)
201
268
  : propertyData?.availability_end
202
- ? moment(propertyData?.availability_end).format(
203
- "YYYY-MM-DD"
204
- )
269
+ ? new Date(propertyData.availability_end)
205
270
  : undefined
206
271
  }
207
- style={{
208
- width: "100%",
209
- padding: "12px",
210
- border: "1px solid #d1d5db",
211
- borderRadius: "8px",
212
- fontSize: "16px",
213
- }}
272
+ excludeDates={excludedDates}
273
+ placeholderText="Select start date"
274
+ dateFormat="yyyy-MM-dd"
275
+ isClearable
214
276
  />
215
277
  </div>
216
278
 
@@ -225,42 +287,31 @@ const BookingModal = ({
225
287
  >
226
288
  Availability End Date
227
289
  </label>
228
- <input
229
- type="date"
290
+ <DatePicker
230
291
  id={`booking_end_${room?.id}`}
231
292
  className="form-input-large"
232
- disabled={room?.to_book === false}
233
- value={
234
- formik?.values?.booking_end
235
- ? moment(formik?.values?.booking_end).format("YYYY-MM-DD")
236
- : ""
293
+ selected={
294
+ formik.values.booking_end
295
+ ? new Date(formik.values.booking_end)
296
+ : null
237
297
  }
238
- onChange={(e) => {
298
+ onChange={(date) => {
239
299
  setError("");
240
300
  formik.setFieldValue("room_id", room?.id);
241
301
  formik.setFieldValue(
242
302
  "price_per_month",
243
303
  room?.price_per_month
244
304
  );
245
- formik.setFieldValue("booking_end", e.target.value);
305
+ formik.setFieldValue("booking_end", date);
246
306
  }}
247
- min={
248
- formik?.values?.booking_start
249
- ? moment(formik?.values?.booking_start)
250
- .add(1, "day")
251
- .format("YYYY-MM-DD")
252
- : room?.to_book === true
253
- ? typeof room.next_available_date === "string"
254
- ? moment(room.next_available_date).format("YYYY-MM-DD")
255
- : moment(
256
- moment(
257
- room?.booking_start ??
258
- propertyData?.availability_start
259
- ).isAfter(moment())
260
- ? room?.booking_start ??
261
- propertyData?.availability_start
262
- : new Date()
263
- ).format("YYYY-MM-DD")
307
+ disabled={room?.to_book === false}
308
+ minDate={
309
+ formik.values.booking_start
310
+ ? moment(formik.values.booking_start)
311
+ .add(1, "days")
312
+ .toDate()
313
+ : room?.next_available_date
314
+ ? new Date(room.next_available_date)
264
315
  : moment(
265
316
  moment(
266
317
  room?.booking_start ??
@@ -269,36 +320,22 @@ const BookingModal = ({
269
320
  ? room?.booking_start ??
270
321
  propertyData?.availability_start
271
322
  : new Date()
272
- ).format("YYYY-MM-DD")
323
+ ).toDate()
273
324
  }
274
- max={
325
+ maxDate={
275
326
  room?.booking_end
276
- ? moment(room?.booking_end).format("YYYY-MM-DD")
327
+ ? new Date(room.booking_end)
277
328
  : propertyData?.availability_end
278
- ? moment(propertyData?.availability_end).format(
279
- "YYYY-MM-DD"
280
- )
329
+ ? new Date(propertyData.availability_end)
281
330
  : undefined
282
331
  }
283
- style={{
284
- width: "100%",
285
- padding: "12px",
286
- border: "1px solid #d1d5db",
287
- borderRadius: "8px",
288
- fontSize: "16px",
289
- }}
332
+ excludeDates={excludedDates}
333
+ placeholderText="Select end date"
334
+ dateFormat="yyyy-MM-dd"
335
+ isClearable
290
336
  />
291
337
  </div>
292
338
 
293
- {error && (
294
- <span
295
- className="error-text"
296
- style={{ color: "#ef4444", fontSize: "14px" }}
297
- >
298
- {error}
299
- </span>
300
- )}
301
-
302
339
  <div className="form-group">
303
340
  <label
304
341
  htmlFor={`number_of_guests_${room?.id}`}
@@ -323,14 +360,6 @@ const BookingModal = ({
323
360
  !formik.values.booking_end ||
324
361
  room?.to_book === false
325
362
  }
326
- style={{
327
- width: "100%",
328
- padding: "12px",
329
- border: "1px solid #d1d5db",
330
- borderRadius: "8px",
331
- fontSize: "16px",
332
- backgroundColor: "white",
333
- }}
334
363
  >
335
364
  <option value="" disabled>
336
365
  Select number of guests
@@ -342,53 +371,69 @@ const BookingModal = ({
342
371
  ))}
343
372
  </select>
344
373
  </div>
374
+
375
+ {error && (
376
+ <div
377
+ className="error-text"
378
+ style={{
379
+ color: "#ef4444",
380
+ fontSize: "14px",
381
+ padding: "8px",
382
+ backgroundColor: "#fef2f2",
383
+ border: "1px solid #fecaca",
384
+ borderRadius: "4px",
385
+ }}
386
+ >
387
+ {error}
388
+ </div>
389
+ )}
345
390
  </div>
346
- </form>
347
391
 
348
- <div
349
- className="price-breakdown"
350
- style={{
351
- marginTop: "24px",
352
- paddingTop: "15px",
353
- borderTop: "1px solid #e5e7eb",
354
- }}
355
- >
356
392
  <div
357
- className="price-row"
393
+ className="price-breakdown"
358
394
  style={{
359
- display: "flex",
360
- justifyContent: "space-between",
361
- alignItems: "center",
395
+ paddingTop: "15px",
396
+ borderTop: "1px solid #e5e7eb",
362
397
  }}
363
398
  >
364
- <div className="info-icon">
365
- <span style={{ fontWeight: "500" }}>Amount</span>
399
+ <div
400
+ className="price-row"
401
+ style={{
402
+ display: "flex",
403
+ justifyContent: "space-between",
404
+ alignItems: "center",
405
+ }}
406
+ >
407
+ <div className="info-icon">
408
+ <span style={{ fontWeight: "500" }}>Amount</span>
409
+ </div>
410
+ <span style={{ fontWeight: "600", fontSize: "18px" }}>
411
+ {`${currencySymbol()} ${
412
+ Number(room?.price_per_month)?.toFixed(2) || 0
413
+ }`}
414
+ </span>
366
415
  </div>
367
- <span style={{ fontWeight: "600", fontSize: "18px" }}>
368
- {`${currencySymbol()} ${
369
- Number(room?.price_per_month)?.toFixed(2) || 0
370
- }`}
371
- </span>
372
416
  </div>
373
- </div>
374
417
 
375
- <div
376
- className="button-group"
377
- style={{ marginTop: "24px", width: "100%" }}
378
- >
379
- <ButtonPrimary
380
- disabled={
381
- isButtonDisabled ||
382
- room?.status === "inactive" ||
383
- room?.to_book === false
384
- }
385
- onClick={formik.handleSubmit}
386
- loading={isLoading}
387
- className="book-now-btn"
418
+ <div
419
+ className="button-group"
420
+ style={{ marginTop: "24px", width: "100%" }}
388
421
  >
389
- Book Now
390
- </ButtonPrimary>
391
- </div>
422
+ <ButtonPrimary
423
+ type="submit"
424
+ disabled={
425
+ isButtonDisabled ||
426
+ room?.status === "inactive" ||
427
+ room?.to_book === false ||
428
+ isLoading
429
+ }
430
+ loading={isLoading}
431
+ className="book-now-btn"
432
+ >
433
+ Book Now
434
+ </ButtonPrimary>
435
+ </div>
436
+ </form>
392
437
  </div>
393
438
  </div>
394
439
  </div>