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
@@ -21,11 +21,14 @@ import successHandler from "../../utils/helpers/SuccessHandler";
21
21
  import Checkbox from "../../shared/Checkbox/Checkbox";
22
22
  import CommonLayout from "./CommonLayout";
23
23
  import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
24
- import CustomDatePicker from "../../shared/DateField/CustomDatePicker";
25
24
  import { useDropzone } from "react-dropzone";
26
25
  import NcImage from "../../shared/NcImage/NcImage";
27
26
  import moment from "moment";
28
27
  import "../../styles/room.scss";
28
+ import DatePicker from "react-datepicker";
29
+ import { roomValidationSchema } from "../../shared/Schema/FormSchema";
30
+ import { showToastError } from "../../utils/helpers/ToastErros";
31
+ import SecondryButton from "../../shared/Button/SecondryButton";
29
32
 
30
33
  const Rooms = () => {
31
34
  const dispatch = useDispatch();
@@ -34,7 +37,8 @@ const Rooms = () => {
34
37
  const { slug } = useParams();
35
38
  const id = slug;
36
39
  const [property, setProperty] = useState(null);
37
-
40
+ const [originalValues, setOrignalValues] = useState(null);
41
+ const [showAddRoomButton, setShowAddRoomButton] = useState(true);
38
42
  const initialRoomValues = {
39
43
  id: "",
40
44
  name: "",
@@ -67,27 +71,26 @@ const Rooms = () => {
67
71
  const [roomImagePreviews, setRoomImagePreviews] = useState({});
68
72
  const [roomImagesToDelete, setRoomImagesToDelete] = useState({});
69
73
  const [isLoading, setIsLoading] = useState(true);
74
+
70
75
  useEffect(() => {
71
76
  if (initialValues?.rooms_attributes?.length > 0) {
72
77
  setCount(initialValues?.rooms_attributes?.length);
73
78
  }
74
79
  }, [initialValues]);
75
-
76
80
  useEffect(() => {
77
81
  if (id) {
78
82
  const fetchPropertyData = async () => {
79
83
  const response = await dispatch(
80
84
  getallupdateProperties({ propertyId: id })
81
85
  ).unwrap();
82
- if (response.payload?.success) {
86
+
87
+ if (response) {
83
88
  setProperty(response);
84
- }
85
- if (response?.rooms) {
86
- const mappedRooms = response?.rooms.map((room) => {
87
- const sortedPlaceImages = [...room?.room_images].sort(
88
- (a, b) => a.position - b.position
89
- );
90
- return {
89
+ // Set total rooms from API, default to 1 if not provided
90
+ setTotalRooms(response.total_rooms || 1);
91
+ let roomsToShow = [];
92
+ if (response?.rooms?.length > 0) {
93
+ roomsToShow = response.rooms.map((room) => ({
91
94
  id: room.id,
92
95
  name: room.name,
93
96
  price_per_month:
@@ -108,27 +111,52 @@ const Rooms = () => {
108
111
  amenities: room.amenities || [],
109
112
  features: room.features || [],
110
113
  room_images: [],
111
- existing_room_images: sortedPlaceImages || [],
114
+ existing_room_images:
115
+ [...room?.room_images].sort(
116
+ (a, b) => a.position - b.position
117
+ ) || [],
112
118
  max_guests: room.max_guests?.toString() || "",
113
- };
114
- });
115
- const uniqueRooms = Array.from(
116
- new Map(mappedRooms.map((room) => [room?.id, room])).values()
119
+ }));
120
+ roomsToShow = Array.from(
121
+ new Map(roomsToShow.map((room) => [room.id, room])).values()
122
+ );
123
+ } else {
124
+ roomsToShow = [{ ...initialRoomValues }];
125
+ }
126
+ const finalValues = {
127
+ rooms_attributes: Array.from(roomsToShow),
128
+ };
129
+
130
+ setInitialValues(finalValues);
131
+ setOrignalValues(JSON.parse(JSON.stringify(finalValues)));
132
+ setShowAddRoomButton(
133
+ roomsToShow.length < (response.total_rooms || 1)
117
134
  );
118
- setInitialValues({
119
- rooms_attributes:
120
- uniqueRooms.length > 0
121
- ? uniqueRooms
122
- : initialValues.rooms_attributes?.map((item) => ({
123
- ...item,
124
- })),
125
- });
126
135
  }
127
136
  };
128
137
  fetchPropertyData();
129
138
  }
130
139
  }, [dispatch, globalId, id]);
131
140
 
141
+ const hasFormChanged = (currentValues, originalValues) => {
142
+ if (!originalValues) return true;
143
+ const currentStr = JSON.stringify(currentValues);
144
+ const originalStr = JSON.stringify(originalValues);
145
+
146
+ return currentStr !== originalStr;
147
+ };
148
+ const normalizeArrayForComparison = (arr) => {
149
+ return (
150
+ arr
151
+ ?.map((item) => ({
152
+ id: item.id || item.amenity_id || item.feature_id,
153
+ amenity_id: item.amenity_id,
154
+ feature_id: item.feature_id,
155
+ }))
156
+ .sort((a, b) => (a.id || 0) - (b.id || 0)) || []
157
+ );
158
+ };
159
+
132
160
  const handleAmenitiesChange = (
133
161
  amenityId,
134
162
  roomIndex,
@@ -181,6 +209,10 @@ const Rooms = () => {
181
209
  };
182
210
 
183
211
  const handleSubmit = async (values) => {
212
+ if (!hasFormChanged(values, originalValues)) {
213
+ navigate(`/listing-stay-detail/${slug}`);
214
+ return;
215
+ }
184
216
  const formData = new FormData();
185
217
 
186
218
  if (id) {
@@ -313,6 +345,38 @@ const Rooms = () => {
313
345
  }
314
346
  };
315
347
 
348
+ const formatFieldName = (field) => {
349
+ return field.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
350
+ };
351
+ const displayValidationErrors = (formErrors) => {
352
+ if (
353
+ formErrors.rooms_attributes &&
354
+ Array.isArray(formErrors.rooms_attributes)
355
+ ) {
356
+ for (let index = 0; index < formErrors.rooms_attributes.length; index++) {
357
+ const roomError = formErrors.rooms_attributes[index];
358
+ if (roomError && typeof roomError === "object") {
359
+ for (const field of Object.keys(roomError)) {
360
+ if (roomError[field]) {
361
+ return [`Room ${index + 1} -${roomError[field]}`];
362
+ }
363
+ }
364
+ }
365
+ }
366
+ }
367
+ for (const key of Object.keys(formErrors)) {
368
+ if (key !== "rooms_attributes") {
369
+ const error = formErrors[key];
370
+ if (typeof error === "string") {
371
+ const formattedKey = formatFieldName(key);
372
+ return [`${formattedKey}: ${error}`];
373
+ }
374
+ }
375
+ }
376
+
377
+ return [];
378
+ };
379
+
316
380
  function arraysEqual(arr1, arr2) {
317
381
  if (arr1.length !== arr2.length) return false;
318
382
  for (let i = 0; i < arr1.length; i++) {
@@ -321,24 +385,6 @@ const Rooms = () => {
321
385
  return true;
322
386
  }
323
387
 
324
- const handleRoomImagesChange = (e, index, setFieldValue, values) => {
325
- const files = e.target.files;
326
- const newFiles = files ? Array.from(files) : [];
327
- if (files) {
328
- const newPreviews = newFiles.map((file) => URL.createObjectURL(file));
329
-
330
- setRoomImagePreviews((prevPreviews) => ({
331
- ...prevPreviews,
332
- [index]: [...(prevPreviews[index] || []), ...newPreviews],
333
- }));
334
-
335
- const existingImages =
336
- values.rooms_attributes?.[index]?.room_images || [];
337
- const combinedImages = [...existingImages, ...newFiles];
338
- setFieldValue(`rooms_attributes[${index}].room_images`, combinedImages);
339
- }
340
- };
341
-
342
388
  const handleRemoveRoomImage = async (
343
389
  previewIndex,
344
390
  index,
@@ -427,53 +473,85 @@ const Rooms = () => {
427
473
  fetchRoomTypes();
428
474
  }, [dispatch]);
429
475
 
430
- const useRoomsImagesDropzone = (index, values, setFieldValue) => {
431
- return useDropzone({
432
- onDrop: (acceptedFiles) => {
433
- const files = acceptedFiles.map((file) => URL.createObjectURL(file));
434
-
435
- setRoomImagePreviews((prevPreviews) => ({
436
- ...prevPreviews,
437
- [index]: [...(prevPreviews[index] || []), ...files],
438
- }));
439
-
440
- const existingImages =
441
- values.rooms_attributes?.[index]?.room_images || [];
442
- const combinedImages = [...existingImages, ...acceptedFiles];
443
- setFieldValue(`rooms_attributes[${index}].room_images`, combinedImages);
444
- },
445
- });
446
- };
447
-
448
476
  const RoomImagesUploader = ({ index }) => {
449
- const { values, setFieldValue } = useFormikContext();
477
+ const { values, setFieldValue, errors, touched } = useFormikContext();
478
+
450
479
  const handleImageUpload = (files) => {
451
480
  if (!files || files.length === 0) return;
452
- const newFiles = Array.from(files);
453
- const newPreviews = newFiles.map((file) => ({
481
+ const allowedTypes = [
482
+ "image/png",
483
+ "image/jpg",
484
+ "image/jpeg",
485
+ "image/gif",
486
+ ];
487
+ const maxSize = 10 * 1024 * 1024; // 10MB
488
+
489
+ const validFiles = [];
490
+ const invalidFiles = [];
491
+
492
+ Array.from(files).forEach((file) => {
493
+ if (!allowedTypes.includes(file.type)) {
494
+ invalidFiles.push(`${file.name}: Invalid file type`);
495
+ } else if (file.size > maxSize) {
496
+ invalidFiles.push(`${file.name}: File too large (max 10MB)`);
497
+ } else {
498
+ validFiles.push(file);
499
+ }
500
+ });
501
+
502
+ if (invalidFiles.length > 0) {
503
+ alert(`Invalid files:\n${invalidFiles.join("\n")}`);
504
+ }
505
+
506
+ if (validFiles.length === 0) return;
507
+ const existingImages =
508
+ values.rooms_attributes?.[index]?.existing_room_images || [];
509
+ const currentImages = values.rooms_attributes?.[index]?.room_images || [];
510
+ const totalImages =
511
+ existingImages.length + currentImages.length + validFiles.length;
512
+
513
+ if (totalImages > 10) {
514
+ alert(
515
+ `Maximum 10 images allowed per room. You can add ${
516
+ 10 - (existingImages.length + currentImages.length)
517
+ } more images.`
518
+ );
519
+ return;
520
+ }
521
+
522
+ const newPreviews = validFiles.map((file) => ({
454
523
  previewUrl: URL.createObjectURL(file),
455
524
  file,
456
525
  }));
457
- setRoomImagePreviews((prevPreviews) => ({
458
- ...prevPreviews,
459
- [index]: [...(prevPreviews[index] || []), ...newPreviews],
460
- }));
461
- const existingImages =
462
- values.rooms_attributes?.[index]?.room_images || [];
463
- const combinedImages = [...existingImages, ...newFiles];
526
+ const combinedImages = [...currentImages, ...validFiles];
464
527
  setFieldValue(`rooms_attributes[${index}].room_images`, combinedImages);
465
528
  };
529
+
466
530
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
467
531
  accept: {
468
532
  "image/*": [".jpeg", ".jpg", ".png", ".gif"],
469
533
  },
470
534
  onDrop: handleImageUpload,
535
+ maxSize: 10 * 1024 * 1024,
471
536
  });
472
-
537
+ const getImageError = () => {
538
+ if (
539
+ errors.rooms_attributes &&
540
+ Array.isArray(errors.rooms_attributes) &&
541
+ errors.rooms_attributes[index] &&
542
+ errors.rooms_attributes[index].room_images
543
+ ) {
544
+ return errors.rooms_attributes[index].room_images;
545
+ }
546
+ return null;
547
+ };
548
+ const imageError = getImageError();
473
549
  return (
474
550
  <div
475
551
  {...getRootProps()}
476
- className={`upload-container ${isDragActive ? "drag-active" : ""}`}
552
+ className={`upload-container ${isDragActive ? "drag-active" : ""} ${
553
+ imageError ? "error" : ""
554
+ }`}
477
555
  >
478
556
  <input
479
557
  {...getInputProps()}
@@ -482,7 +560,6 @@ const Rooms = () => {
482
560
  type="file"
483
561
  className="sr-only"
484
562
  multiple
485
- // No need for onChange here as the dropzone will handle all inputs
486
563
  />
487
564
  <div className="upload-content">
488
565
  <svg
@@ -505,6 +582,7 @@ const Rooms = () => {
505
582
  </div>
506
583
  <p className="upload-info">PNG, JPG, GIF up to 10MB</p>
507
584
  </div>
585
+ {imageError && <div className="error-message">{imageError}</div>}
508
586
  </div>
509
587
  );
510
588
  };
@@ -524,7 +602,7 @@ const Rooms = () => {
524
602
  <div className="divider" />
525
603
  <Formik
526
604
  initialValues={initialValues}
527
- // validationSchema={roomRuleValidationSchema}
605
+ validationSchema={roomValidationSchema}
528
606
  onSubmit={handleSubmit}
529
607
  enableReinitialize={true}
530
608
  >
@@ -536,477 +614,421 @@ const Rooms = () => {
536
614
  isSubmitting,
537
615
  getFieldProps,
538
616
  setFieldValue,
539
- }) => (
540
- <form onSubmit={handleSubmit} className="form-container">
541
- {values.rooms_attributes.map((room, index) => (
542
- <div
543
- key={index}
544
- className={`room-section ${
545
- index > 0 ? "room-section-bordered" : ""
546
- }`}
547
- >
548
- <h3 className="room-title">Room {index + 1}</h3>
549
- <FormItem label="Room Name" className="form-item">
550
- <Input
551
- type="text"
552
- placeholder="Enter Room name"
553
- {...getFieldProps(`rooms_attributes[${index}].name`)}
554
- className="common-input"
555
- />
556
- {errors.rooms_attributes &&
557
- Array.isArray(errors.rooms_attributes) &&
558
- typeof errors.rooms_attributes[index] === "object" &&
559
- errors.rooms_attributes[index] !== null &&
560
- errors.rooms_attributes[index].name && (
561
- <div className="error-text">
562
- {errors.rooms_attributes[index].name}
563
- </div>
564
- )}
565
- </FormItem>
566
- <FormItem label="Price per Month" className="form-item">
567
- <div className="relative">
568
- <Input
569
- type="text"
570
- name={`rooms_attributes[${index}].price_per_month`}
571
- placeholder="Please enter price per month"
572
- onChange={handleChange}
573
- value={values.rooms_attributes[index]?.price_per_month}
574
- className="common-input"
575
- />
576
- </div>
577
- {errors.rooms_attributes &&
578
- Array.isArray(errors.rooms_attributes) &&
579
- typeof errors.rooms_attributes[index] === "object" &&
580
- errors.rooms_attributes[index] !== null &&
581
- errors.rooms_attributes[index].price_per_month && (
582
- <div className="error-text">
583
- {errors.rooms_attributes[index].price_per_month}
584
- </div>
585
- )}
586
- </FormItem>
587
- <FormItem label="Status" className="form-item">
588
- <Select
589
- name={`rooms_attributes[${index}].status`}
590
- onChange={handleChange}
591
- value={room.status}
592
- className="common-input"
593
- >
594
- <option value="">Select status</option>
595
- <option value="active">Active</option>
596
- <option value="inactive">Inactive</option>
597
- </Select>
598
- {errors.rooms_attributes &&
599
- Array.isArray(errors.rooms_attributes) &&
600
- typeof errors.rooms_attributes[index] === "object" &&
601
- errors.rooms_attributes[index] !== null &&
602
- errors.rooms_attributes[index].status && (
603
- <div className="error-text">
604
- {errors.rooms_attributes[index].status}
605
- </div>
606
- )}
607
- </FormItem>
608
- <FormItem label="Max Guests" className="form-item">
609
- <div className="relative">
617
+ validateForm,
618
+ }) => {
619
+ const handleFormSubmit = async (e) => {
620
+ e.preventDefault();
621
+ const formErrors = await validateForm();
622
+ if (Object.keys(formErrors).length > 0) {
623
+ const errorMessages = displayValidationErrors(formErrors);
624
+ if (errorMessages.length > 0) {
625
+ showToastError(errorMessages[0]);
626
+ return;
627
+ }
628
+ }
629
+ await handleSubmit(values);
630
+ };
631
+
632
+ return (
633
+ <form onSubmit={handleFormSubmit} className="form-container">
634
+ {values.rooms_attributes.map((room, index) => (
635
+ <div
636
+ key={index}
637
+ className={`room-section ${
638
+ index > 0 ? "room-section-bordered" : ""
639
+ }`}
640
+ >
641
+ <h3 className="room-title">Room {index + 1}</h3>
642
+ <FormItem label="Room Name" className="form-item">
610
643
  <Input
611
644
  type="text"
612
- name={`rooms_attributes[${index}].max_guests`}
613
- placeholder="Max-Guests"
614
- onChange={handleChange}
615
- value={values.rooms_attributes[index]?.max_guests}
616
- className="common-input"
617
- />
618
- </div>
619
- {errors.rooms_attributes &&
620
- Array.isArray(errors.rooms_attributes) &&
621
- typeof errors.rooms_attributes[index] === "object" &&
622
- errors.rooms_attributes[index] !== null &&
623
- errors.rooms_attributes[index].max_guests && (
624
- <div className="error-text">
625
- {errors.rooms_attributes[index].max_guests}
626
- </div>
627
- )}
628
- </FormItem>
629
- <div className="date-picker-container">
630
- <FormItem label="Availability Start Date">
631
- <input
632
- type="date"
645
+ placeholder="Enter Room name"
646
+ {...getFieldProps(`rooms_attributes[${index}].name`)}
633
647
  className="common-input"
634
- value={
635
- room.booking_start
636
- ? room.booking_start.split("T")[0]
637
- : ""
638
- }
639
- onChange={(e) => {
640
- const selectedDate = e.target.value;
641
- setFieldValue(
642
- `rooms_attributes[${index}].booking_start`,
643
- selectedDate
644
- );
645
- setFieldValue(
646
- `rooms_attributes[${index}].booking_end`,
647
- null
648
- );
649
- }}
650
- min={new Date().toISOString().split("T")[0]}
651
648
  />
652
649
  {errors.rooms_attributes &&
653
650
  Array.isArray(errors.rooms_attributes) &&
654
- errors.rooms_attributes[index] &&
655
651
  typeof errors.rooms_attributes[index] === "object" &&
656
652
  errors.rooms_attributes[index] !== null &&
657
- errors.rooms_attributes[index].booking_start && (
653
+ errors.rooms_attributes[index].name && (
658
654
  <div className="error-text">
659
- {typeof errors.rooms_attributes[index]
660
- .booking_start === "string" &&
661
- errors.rooms_attributes[index].booking_start}
655
+ {errors.rooms_attributes[index].name}
662
656
  </div>
663
657
  )}
664
658
  </FormItem>
665
-
666
- <FormItem
667
- label="Availability End Date"
668
- className="form-item"
669
- >
670
- <input
671
- type="date"
672
- className="common-input"
673
- value={
674
- room.booking_end ? room.booking_end.split("T")[0] : ""
675
- }
676
- onChange={(e) => {
677
- const selectedDate = e.target.value;
678
- setFieldValue(
679
- `rooms_attributes[${index}].booking_end`,
680
- selectedDate
681
- );
682
- }}
683
- min={
684
- room.booking_start
685
- ? new Date(
686
- new Date(room.booking_start).getTime() +
687
- 86400000
688
- )
689
- .toISOString()
690
- .split("T")[0]
691
- : new Date().toISOString().split("T")[0]
692
- }
693
- />
659
+ <FormItem label="Price per Month" className="form-item">
660
+ <div className="relative">
661
+ <Input
662
+ type="text"
663
+ name={`rooms_attributes[${index}].price_per_month`}
664
+ placeholder="Please enter price per month"
665
+ onChange={handleChange}
666
+ value={
667
+ values.rooms_attributes[index]?.price_per_month
668
+ }
669
+ className="common-input"
670
+ />
671
+ </div>
694
672
  {errors.rooms_attributes &&
695
673
  Array.isArray(errors.rooms_attributes) &&
696
- errors.rooms_attributes[index] &&
697
674
  typeof errors.rooms_attributes[index] === "object" &&
698
675
  errors.rooms_attributes[index] !== null &&
699
- errors.rooms_attributes[index].booking_end && (
676
+ errors.rooms_attributes[index].price_per_month && (
700
677
  <div className="error-text">
701
- {typeof errors.rooms_attributes[index]
702
- .booking_end === "string" &&
703
- errors.rooms_attributes[index].booking_end}
678
+ {errors.rooms_attributes[index].price_per_month}
704
679
  </div>
705
680
  )}
706
681
  </FormItem>
707
- </div>
708
- <FormItem label="Description" className="form-item">
709
- <textarea
710
- className="description-textarea"
711
- name={`rooms_attributes[${index}].description`}
712
- placeholder="Enter room description"
713
- onChange={handleChange}
714
- value={room.description}
715
- />
716
- {errors.rooms_attributes &&
717
- Array.isArray(errors.rooms_attributes) &&
718
- typeof errors.rooms_attributes[index] === "object" &&
719
- errors.rooms_attributes[index] !== null &&
720
- errors.rooms_attributes[index].description && (
721
- <div className="error-text">
722
- {errors.rooms_attributes[index].description}
723
- </div>
724
- )}
725
- </FormItem>
726
- <div className="room-details-container">
727
- <FormItem label="Bed Type" className="form-item">
682
+ <FormItem label="Status" className="form-item">
728
683
  <Select
729
- name={`rooms_attributes[${index}].bed_type_id`}
684
+ name={`rooms_attributes[${index}].status`}
730
685
  onChange={handleChange}
731
- value={room.bed_type_id}
686
+ value={room.status}
732
687
  className="common-input"
733
688
  >
734
- <option value="">Select bed type</option>
735
- {bedTypes.map((bedType) => (
736
- <option key={bedType.id} value={bedType.id}>
737
- {bedType.name}
738
- </option>
739
- ))}
689
+ <option value="">Select status</option>
690
+ <option value="active">Active</option>
691
+ <option value="inactive">Inactive</option>
740
692
  </Select>
741
693
  {errors.rooms_attributes &&
742
694
  Array.isArray(errors.rooms_attributes) &&
743
695
  typeof errors.rooms_attributes[index] === "object" &&
744
696
  errors.rooms_attributes[index] !== null &&
745
- errors.rooms_attributes[index].bed_type_id && (
697
+ errors.rooms_attributes[index].status && (
746
698
  <div className="error-text">
747
- {errors.rooms_attributes[index].bed_type_id}
699
+ {errors.rooms_attributes[index].status}
748
700
  </div>
749
701
  )}
750
702
  </FormItem>
751
- <FormItem label="Room Size (sq ft)" className="form-item">
752
- <Input
753
- type="text"
754
- name={`rooms_attributes[${index}].size`}
755
- placeholder="Enter room size"
756
- onChange={handleChange}
757
- value={room.size}
758
- className="common-input"
759
- />
703
+ <FormItem label="Max Guests" className="form-item">
704
+ <div className="relative">
705
+ <Input
706
+ type="text"
707
+ name={`rooms_attributes[${index}].max_guests`}
708
+ placeholder="Max-Guests"
709
+ onChange={handleChange}
710
+ value={values.rooms_attributes[index]?.max_guests}
711
+ className="common-input"
712
+ />
713
+ </div>
760
714
  {errors.rooms_attributes &&
761
715
  Array.isArray(errors.rooms_attributes) &&
762
716
  typeof errors.rooms_attributes[index] === "object" &&
763
717
  errors.rooms_attributes[index] !== null &&
764
- errors.rooms_attributes[index].size && (
718
+ errors.rooms_attributes[index].max_guests && (
765
719
  <div className="error-text">
766
- {errors.rooms_attributes[index].size}
720
+ {errors.rooms_attributes[index].max_guests}
767
721
  </div>
768
722
  )}
769
723
  </FormItem>
770
- <FormItem label="Room Type" className="form-item">
771
- <Select
772
- name={`rooms_attributes[${index}].room_type_id`}
773
- onChange={handleChange}
774
- value={room.room_type_id}
775
- className="common-input"
724
+ <div className="date-picker-container">
725
+ <FormItem label="Availability Start Date">
726
+ <DatePicker
727
+ selected={
728
+ room.booking_start
729
+ ? new Date(room.booking_start)
730
+ : null
731
+ }
732
+ onChange={(date) => {
733
+ setFieldValue(
734
+ `rooms_attributes[${index}].booking_start`,
735
+ date
736
+ );
737
+ setFieldValue(
738
+ `rooms_attributes[${index}].booking_end`,
739
+ null
740
+ );
741
+ }}
742
+ minDate={new Date()}
743
+ placeholderText="Select start date"
744
+ dateFormat="yyyy-MM-dd"
745
+ className="common-input"
746
+ isClearable
747
+ />
748
+ {errors.rooms_attributes &&
749
+ Array.isArray(errors.rooms_attributes) &&
750
+ errors.rooms_attributes[index] &&
751
+ typeof errors.rooms_attributes[index] === "object" &&
752
+ errors.rooms_attributes[index] !== null &&
753
+ errors.rooms_attributes[index].booking_start && (
754
+ <div className="error-text">
755
+ {errors.rooms_attributes[index].booking_start}
756
+ </div>
757
+ )}
758
+ </FormItem>
759
+
760
+ <FormItem
761
+ label="Availability End Date"
762
+ className="form-item"
776
763
  >
777
- <option value="">Select room type</option>
778
- {roomTypes.map((roomType) => (
779
- <option key={roomType.id} value={roomType.id}>
780
- {roomType.name}
781
- </option>
782
- ))}
783
- </Select>
764
+ <DatePicker
765
+ selected={
766
+ room.booking_end ? new Date(room.booking_end) : null
767
+ }
768
+ onChange={(date) =>
769
+ setFieldValue(
770
+ `rooms_attributes[${index}].booking_end`,
771
+ date
772
+ )
773
+ }
774
+ minDate={
775
+ room.booking_start
776
+ ? moment(room.booking_start)
777
+ .add(1, "days")
778
+ .toDate()
779
+ : new Date()
780
+ }
781
+ placeholderText="Select end date"
782
+ dateFormat="yyyy-MM-dd"
783
+ className="common-input"
784
+ isClearable
785
+ />
786
+ {errors.rooms_attributes &&
787
+ Array.isArray(errors.rooms_attributes) &&
788
+ errors.rooms_attributes[index] &&
789
+ typeof errors.rooms_attributes[index] === "object" &&
790
+ errors.rooms_attributes[index] !== null &&
791
+ errors.rooms_attributes[index].booking_end && (
792
+ <div className="error-text">
793
+ {errors.rooms_attributes[index].booking_end}
794
+ </div>
795
+ )}
796
+ </FormItem>
797
+ </div>
798
+ <FormItem label="Description" className="form-item">
799
+ <textarea
800
+ className="description-textarea"
801
+ name={`rooms_attributes[${index}].description`}
802
+ placeholder="Enter room description"
803
+ onChange={handleChange}
804
+ value={room.description}
805
+ />
784
806
  {errors.rooms_attributes &&
785
807
  Array.isArray(errors.rooms_attributes) &&
786
808
  typeof errors.rooms_attributes[index] === "object" &&
787
809
  errors.rooms_attributes[index] !== null &&
788
- errors.rooms_attributes[index].room_type_id && (
810
+ errors.rooms_attributes[index].description && (
789
811
  <div className="error-text">
790
- {errors.rooms_attributes[index].room_type_id}
812
+ {errors.rooms_attributes[index].description}
791
813
  </div>
792
814
  )}
793
815
  </FormItem>
794
- </div>
795
- <FormItem>
796
- <h2 className="section-title">Amenities</h2>
797
- <div className="checkbox-grid">
798
- {amenities.map((amenity) => (
799
- <Checkbox
800
- key={amenity.id.toString()}
801
- id={amenity.id.toString()}
802
- label={amenity.name}
803
- name={`rooms_attributes[${index}].amenities`}
804
- onChange={(checked) =>
805
- handleAmenitiesChange(
806
- amenity?.id,
807
- index,
808
- values,
809
- setFieldValue
810
- )
811
- }
812
- checked={values.rooms_attributes[
813
- index
814
- ]?.amenities?.some(
815
- (item) =>
816
- item.amenity_id === amenity.id ||
817
- item.id === amenity.id
816
+ <div className="room-details-container">
817
+ <FormItem label="Bed Type" className="form-item">
818
+ <Select
819
+ name={`rooms_attributes[${index}].bed_type_id`}
820
+ onChange={handleChange}
821
+ value={room.bed_type_id}
822
+ className="common-input"
823
+ >
824
+ <option value="">Select bed type</option>
825
+ {bedTypes.map((bedType) => (
826
+ <option key={bedType.id} value={bedType.id}>
827
+ {bedType.name}
828
+ </option>
829
+ ))}
830
+ </Select>
831
+ {errors.rooms_attributes &&
832
+ Array.isArray(errors.rooms_attributes) &&
833
+ typeof errors.rooms_attributes[index] === "object" &&
834
+ errors.rooms_attributes[index] !== null &&
835
+ errors.rooms_attributes[index].bed_type_id && (
836
+ <div className="error-text">
837
+ {errors.rooms_attributes[index].bed_type_id}
838
+ </div>
818
839
  )}
840
+ </FormItem>
841
+ <FormItem label="Room Size (sq ft)" className="form-item">
842
+ <Input
843
+ type="text"
844
+ name={`rooms_attributes[${index}].size`}
845
+ placeholder="Enter room size"
846
+ onChange={handleChange}
847
+ value={room.size}
848
+ className="common-input"
819
849
  />
820
- ))}
821
- </div>
822
- </FormItem>
823
- <FormItem>
824
- <h2 className="section-title">Features</h2>
825
- <div className="checkbox-grid">
826
- {features.map((feature) => (
827
- <Checkbox
828
- key={feature.id.toString()}
829
- id={feature.id.toString()}
830
- label={feature.name}
850
+ {errors.rooms_attributes &&
851
+ Array.isArray(errors.rooms_attributes) &&
852
+ typeof errors.rooms_attributes[index] === "object" &&
853
+ errors.rooms_attributes[index] !== null &&
854
+ errors.rooms_attributes[index].size && (
855
+ <div className="error-text">
856
+ {errors.rooms_attributes[index].size}
857
+ </div>
858
+ )}
859
+ </FormItem>
860
+ <FormItem label="Room Type" className="form-item">
861
+ <Select
862
+ name={`rooms_attributes[${index}].room_type_id`}
863
+ onChange={handleChange}
864
+ value={room.room_type_id}
831
865
  className="common-input"
832
- name={`rooms_attributes[${index}].features`}
833
- onChange={(checked) =>
834
- handleFeaturesChange(
835
- feature?.id,
836
- index,
837
- values,
838
- setFieldValue
839
- )
840
- }
841
- checked={values.rooms_attributes[
842
- index
843
- ]?.features?.some(
844
- (item) =>
845
- item.feature_id === feature.id ||
846
- item.id === feature.id
866
+ >
867
+ <option value="">Select room type</option>
868
+ {roomTypes.map((roomType) => (
869
+ <option key={roomType.id} value={roomType.id}>
870
+ {roomType.name}
871
+ </option>
872
+ ))}
873
+ </Select>
874
+ {errors.rooms_attributes &&
875
+ Array.isArray(errors.rooms_attributes) &&
876
+ typeof errors.rooms_attributes[index] === "object" &&
877
+ errors.rooms_attributes[index] !== null &&
878
+ errors.rooms_attributes[index].room_type_id && (
879
+ <div className="error-text">
880
+ {errors.rooms_attributes[index].room_type_id}
881
+ </div>
847
882
  )}
848
- />
849
- ))}
883
+ </FormItem>
850
884
  </div>
851
- </FormItem>
852
- <div key={index}>
853
- <div className="image-upload-section">
854
- <span className="image-upload-title">Room images</span>
855
- <div className="image-upload-content">
856
- <RoomImagesUploader key={index} index={index} />
857
- {isLoading ? (
858
- <div className="image-preview-container">
859
- {Array.from({
860
- length: room.existing_room_images?.length,
861
- }).map((_, previewIndex) => (
862
- <div
863
- key={previewIndex}
864
- className="image-placeholder"
865
- ></div>
866
- ))}
867
- </div>
868
- ) : (
869
- <DragDropContext
870
- onDragEnd={(result) => {
871
- const { source, destination } = result;
872
- if (!destination) return;
873
- const existingImages =
874
- room.existing_room_images || [];
875
- const newImages = room.room_images || [];
876
- const combinedImages = [
877
- ...existingImages,
878
- ...newImages,
879
- ];
885
+ <FormItem>
886
+ <h2 className="section-title">Amenities</h2>
887
+ <div className="checkbox-grid">
888
+ {amenities?.map((amenity) => (
889
+ <Checkbox
890
+ key={amenity.id.toString()}
891
+ id={amenity.id.toString()}
892
+ label={amenity.name}
893
+ name={`rooms_attributes[${index}].amenities`}
894
+ onChange={(checked) =>
895
+ handleAmenitiesChange(
896
+ amenity?.id,
897
+ index,
898
+ values,
899
+ setFieldValue
900
+ )
901
+ }
902
+ checked={values.rooms_attributes[
903
+ index
904
+ ]?.amenities?.some(
905
+ (item) =>
906
+ item.amenity_id === amenity.id ||
907
+ item.id === amenity.id
908
+ )}
909
+ />
910
+ ))}
911
+ </div>
912
+ </FormItem>
913
+ <FormItem>
914
+ <h2 className="section-title">Features</h2>
915
+ <div className="checkbox-grid">
916
+ {features?.map((feature) => (
917
+ <Checkbox
918
+ key={feature.id.toString()}
919
+ id={feature.id.toString()}
920
+ label={feature.name}
921
+ name={`rooms_attributes[${index}].features`}
922
+ onChange={(checked) =>
923
+ handleFeaturesChange(
924
+ feature?.id,
925
+ index,
926
+ values,
927
+ setFieldValue
928
+ )
929
+ }
930
+ checked={values.rooms_attributes[
931
+ index
932
+ ]?.features?.some(
933
+ (item) =>
934
+ item.feature_id === feature.id ||
935
+ item.id === feature.id
936
+ )}
937
+ />
938
+ ))}
939
+ </div>
940
+ </FormItem>
941
+ <div key={index}>
942
+ <div className="image-upload-section">
943
+ <span className="image-upload-title">Room images</span>
944
+ <div className="image-upload-content">
945
+ <RoomImagesUploader key={index} index={index} />
946
+ {isLoading ? (
947
+ <div className="image-preview-container">
948
+ {Array.from({
949
+ length: room.existing_room_images?.length,
950
+ }).map((_, previewIndex) => (
951
+ <div
952
+ key={previewIndex}
953
+ className="image-placeholder"
954
+ ></div>
955
+ ))}
956
+ </div>
957
+ ) : (
958
+ <DragDropContext
959
+ onDragEnd={(result) => {
960
+ const { source, destination } = result;
961
+ if (!destination) return;
962
+ const existingImages =
963
+ room.existing_room_images || [];
964
+ const newImages = room.room_images || [];
965
+ const combinedImages = [
966
+ ...existingImages,
967
+ ...newImages,
968
+ ];
880
969
 
881
- // Reorder the images
882
- const reorderedImages = [...combinedImages];
883
- const [removed] = reorderedImages.splice(
884
- source.index,
885
- 1
886
- );
887
- reorderedImages.splice(
888
- destination.index,
889
- 0,
890
- removed
891
- );
892
- const newPosition = destination.index + 1;
893
- if (removed.id) {
894
- setReorderImages((prev) => {
895
- const updated = prev.filter(
896
- (item) =>
897
- item.position !== newPosition ||
898
- item.blob_id === removed.id
899
- );
900
- return [
901
- ...updated,
902
- {
903
- blob_id: removed.id,
904
- position: newPosition,
905
- },
906
- ];
907
- });
908
- }
970
+ // Reorder the images
971
+ const reorderedImages = [...combinedImages];
972
+ const [removed] = reorderedImages.splice(
973
+ source.index,
974
+ 1
975
+ );
976
+ reorderedImages.splice(
977
+ destination.index,
978
+ 0,
979
+ removed
980
+ );
981
+ const newPosition = destination.index + 1;
982
+ if (removed.id) {
983
+ setReorderImages((prev) => {
984
+ const updated = prev.filter(
985
+ (item) =>
986
+ item.position !== newPosition ||
987
+ item.blob_id === removed.id
988
+ );
989
+ return [
990
+ ...updated,
991
+ {
992
+ blob_id: removed.id,
993
+ position: newPosition,
994
+ },
995
+ ];
996
+ });
997
+ }
909
998
 
910
- const updatedExistingRoomImages =
911
- reorderedImages.filter((item) => item.id);
912
- const updatedRoomImages = reorderedImages.filter(
913
- (item) => !item.id
914
- );
915
- setFieldValue(
916
- `rooms_attributes[${index}].existing_room_images`,
917
- updatedExistingRoomImages
918
- );
919
- setFieldValue(
920
- `rooms_attributes[${index}].room_images`,
921
- updatedRoomImages
922
- );
923
- }}
924
- >
925
- <Droppable
926
- droppableId={`room-images-${index}`}
927
- direction="horizontal"
999
+ const updatedExistingRoomImages =
1000
+ reorderedImages.filter((item) => item.id);
1001
+ const updatedRoomImages =
1002
+ reorderedImages.filter((item) => !item.id);
1003
+ setFieldValue(
1004
+ `rooms_attributes[${index}].existing_room_images`,
1005
+ updatedExistingRoomImages
1006
+ );
1007
+ setFieldValue(
1008
+ `rooms_attributes[${index}].room_images`,
1009
+ updatedRoomImages
1010
+ );
1011
+ }}
928
1012
  >
929
- {(provided) => (
930
- <div
931
- className="image-preview-container"
932
- {...provided.droppableProps}
933
- ref={provided.innerRef}
934
- >
935
- {(room.existing_room_images || []).map(
936
- (image, previewIndex) => (
937
- <Draggable
938
- key={`existing-image-${
939
- image.id || previewIndex
940
- }`}
941
- draggableId={`existing-image-${
942
- image.id || previewIndex
943
- }`}
944
- index={previewIndex}
945
- >
946
- {(provided) => (
947
- <div
948
- ref={provided.innerRef}
949
- {...provided.draggableProps}
950
- {...provided.dragHandleProps}
951
- className="image-wrapper"
952
- >
953
- <div className="image-container">
954
- <NcImage
955
- src={image.url || image}
956
- alt={`Room ${index + 1} Image ${
957
- previewIndex + 1
958
- }`}
959
- className="image"
960
- />
961
- </div>
962
- <button
963
- type="button"
964
- onClick={() =>
965
- handleRemoveRoomImage(
966
- previewIndex,
967
- index,
968
- values,
969
- setFieldValue
970
- )
971
- }
972
- className="remove-image-button"
973
- >
974
- <svg
975
- className="remove-image-icon"
976
- xmlns="http://www.w3.org/2000/svg"
977
- fill="none"
978
- viewBox="0 0 24 24"
979
- stroke="currentColor"
980
- >
981
- <path
982
- strokeLinecap="round"
983
- strokeLinejoin="round"
984
- strokeWidth={2}
985
- d="M6 18L18 6M6 6l12 12"
986
- />
987
- </svg>
988
- </button>
989
- </div>
990
- )}
991
- </Draggable>
992
- )
993
- )}
994
- {(room.room_images || []).map(
995
- (image, newIndex) => {
996
- const previewIndex =
997
- (room.existing_room_images?.length ||
998
- 0) + newIndex;
999
- const previewUrl =
1000
- roomImagePreviews[index]?.[newIndex]
1001
- ?.previewUrl ||
1002
- (image instanceof File
1003
- ? URL.createObjectURL(image)
1004
- : null);
1005
-
1006
- return (
1013
+ <Droppable
1014
+ droppableId={`room-images-${index}`}
1015
+ direction="horizontal"
1016
+ >
1017
+ {(provided) => (
1018
+ <div
1019
+ className="image-preview-container"
1020
+ {...provided.droppableProps}
1021
+ ref={provided.innerRef}
1022
+ >
1023
+ {(room.existing_room_images || []).map(
1024
+ (image, previewIndex) => (
1007
1025
  <Draggable
1008
- key={`new-image-${previewIndex}`}
1009
- draggableId={`new-image-${previewIndex}`}
1026
+ key={`existing-image-${
1027
+ image.id || previewIndex
1028
+ }`}
1029
+ draggableId={`existing-image-${
1030
+ image.id || previewIndex
1031
+ }`}
1010
1032
  index={previewIndex}
1011
1033
  >
1012
1034
  {(provided) => (
@@ -1018,7 +1040,7 @@ const Rooms = () => {
1018
1040
  >
1019
1041
  <div className="image-container">
1020
1042
  <NcImage
1021
- src={previewUrl || image}
1043
+ src={image.url || image}
1022
1044
  alt={`Room ${
1023
1045
  index + 1
1024
1046
  } Image ${previewIndex + 1}`}
@@ -1055,74 +1077,143 @@ const Rooms = () => {
1055
1077
  </div>
1056
1078
  )}
1057
1079
  </Draggable>
1058
- );
1059
- }
1060
- )}
1061
- {provided.placeholder}
1062
- </div>
1063
- )}
1064
- </Droppable>
1065
- </DragDropContext>
1066
- )}
1080
+ )
1081
+ )}
1082
+ {(room.room_images || []).map(
1083
+ (image, newIndex) => {
1084
+ const previewIndex =
1085
+ (room.existing_room_images?.length ||
1086
+ 0) + newIndex;
1087
+ const previewUrl =
1088
+ roomImagePreviews[index]?.[newIndex]
1089
+ ?.previewUrl ||
1090
+ (image instanceof File
1091
+ ? URL.createObjectURL(image)
1092
+ : null);
1093
+
1094
+ return (
1095
+ <Draggable
1096
+ key={`new-image-${previewIndex}`}
1097
+ draggableId={`new-image-${previewIndex}`}
1098
+ index={previewIndex}
1099
+ >
1100
+ {(provided) => (
1101
+ <div
1102
+ ref={provided.innerRef}
1103
+ {...provided.draggableProps}
1104
+ {...provided.dragHandleProps}
1105
+ className="image-wrapper"
1106
+ >
1107
+ <div className="image-container">
1108
+ <NcImage
1109
+ src={previewUrl || image}
1110
+ alt={`Room ${
1111
+ index + 1
1112
+ } Image ${
1113
+ previewIndex + 1
1114
+ }`}
1115
+ className="image"
1116
+ />
1117
+ </div>
1118
+ <button
1119
+ type="button"
1120
+ onClick={() =>
1121
+ handleRemoveRoomImage(
1122
+ previewIndex,
1123
+ index,
1124
+ values,
1125
+ setFieldValue
1126
+ )
1127
+ }
1128
+ className="remove-image-button"
1129
+ >
1130
+ <svg
1131
+ className="remove-image-icon"
1132
+ xmlns="http://www.w3.org/2000/svg"
1133
+ fill="none"
1134
+ viewBox="0 0 24 24"
1135
+ stroke="currentColor"
1136
+ >
1137
+ <path
1138
+ strokeLinecap="round"
1139
+ strokeLinejoin="round"
1140
+ strokeWidth={2}
1141
+ d="M6 18L18 6M6 6l12 12"
1142
+ />
1143
+ </svg>
1144
+ </button>
1145
+ </div>
1146
+ )}
1147
+ </Draggable>
1148
+ );
1149
+ }
1150
+ )}
1151
+ {provided.placeholder}
1152
+ </div>
1153
+ )}
1154
+ </Droppable>
1155
+ </DragDropContext>
1156
+ )}
1157
+ </div>
1067
1158
  </div>
1068
1159
  </div>
1069
1160
  </div>
1161
+ ))}
1162
+ <div
1163
+ className="room-actions-container"
1164
+ style={{
1165
+ display: "flex",
1166
+ gap: "10px",
1167
+ flexWrap: "wrap",
1168
+ }}
1169
+ >
1170
+ {values.rooms_attributes.length > 1 && (
1171
+ <ButtonPrimary
1172
+ type="button"
1173
+ onClick={() => {
1174
+ const newRooms = values.rooms_attributes.slice(0, -1);
1175
+ setFieldValue("rooms_attributes", newRooms);
1176
+ setShowAddRoomButton(newRooms.length < totalRooms);
1177
+ }}
1178
+ className="action-button"
1179
+ >
1180
+ Remove Last Room
1181
+ </ButtonPrimary>
1182
+ )}
1183
+
1184
+ {showAddRoomButton && (
1185
+ <ButtonPrimary
1186
+ type="button"
1187
+ onClick={() => {
1188
+ const newRooms = [
1189
+ ...values.rooms_attributes,
1190
+ initialRoomValues,
1191
+ ];
1192
+ setFieldValue("rooms_attributes", newRooms);
1193
+ setShowAddRoomButton(newRooms.length < totalRooms);
1194
+ }}
1195
+ className="action-button"
1196
+ >
1197
+ Add Another Room
1198
+ </ButtonPrimary>
1199
+ )}
1070
1200
  </div>
1071
- ))}
1072
- <div
1073
- className="room-actions-container"
1074
- style={{
1075
- display: "flex",
1076
- gap: "10px",
1077
- flexWrap: "wrap",
1078
- }}
1079
- >
1080
- {values.rooms_attributes.length > 1 && (
1201
+
1202
+ <div className="form-actions">
1203
+ <SecondryButton href={`/property-4/${slug}`}>
1204
+ Go back
1205
+ </SecondryButton>
1081
1206
  <ButtonPrimary
1082
- type="button"
1083
- onClick={() => {
1084
- setCount((prev) => prev - 1);
1085
- setFieldValue(
1086
- "rooms_attributes",
1087
- values.rooms_attributes.filter(
1088
- (_, idx) => idx !== values.rooms_attributes.length - 1
1089
- )
1090
- );
1091
- }}
1092
- className="action-button"
1207
+ type="submit"
1208
+ onClick={() => handleFormSubmit()}
1209
+ disabled={isSubmitting}
1093
1210
  >
1094
- Remove Last Room
1211
+ {isSubmitting ? "Loading..." : "Continue"}
1095
1212
  </ButtonPrimary>
1096
- )}
1097
- <ButtonPrimary
1098
- type="button"
1099
- onClick={() => {
1100
- setCount((prev) => prev + 1);
1101
- setFieldValue("rooms_attributes", [
1102
- ...values.rooms_attributes,
1103
- initialRoomValues,
1104
- ]);
1105
- }}
1106
- className="action-button"
1107
- >
1108
- Add Another Room
1109
- </ButtonPrimary>
1110
- </div>
1111
-
1112
- <div className="form-actions">
1113
- <ButtonPrimary href={`/property-4/${slug}`}>
1114
- Go back
1115
- </ButtonPrimary>
1116
- <ButtonPrimary
1117
- type="submit"
1118
- onClick={handleSubmit}
1119
- disabled={isSubmitting}
1120
- >
1121
- {isSubmitting ? "Loading..." : "Continue"}
1122
- </ButtonPrimary>
1123
- </div>
1124
- </form>
1125
- )}
1213
+ </div>
1214
+ </form>
1215
+ );
1216
+ }}
1126
1217
  </Formik>
1127
1218
  </div>
1128
1219
  </CommonLayout>