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
@@ -12,12 +12,8 @@ import "../../styles/images.scss";
|
|
12
12
|
import ButtonPrimary from "../../shared/Button/ButtonPrimary";
|
13
13
|
import { useDropzone } from "react-dropzone";
|
14
14
|
import successHandler from "../../utils/helpers/SuccessHandler";
|
15
|
-
|
16
|
-
|
17
|
-
place_images: Yup.array()
|
18
|
-
.min(1, "Please upload at least one image")
|
19
|
-
.required("Images are required"),
|
20
|
-
});
|
15
|
+
import { ImagevalidationSchema } from "../../shared/Schema/FormSchema";
|
16
|
+
import SecondryButton from "../../shared/Button/SecondryButton";
|
21
17
|
|
22
18
|
const Images = () => {
|
23
19
|
const dispatch = useDispatch();
|
@@ -28,6 +24,7 @@ const Images = () => {
|
|
28
24
|
const { PropertyToEdit } = useSelector((state) => state.property);
|
29
25
|
const [previews, setPreviews] = useState([]);
|
30
26
|
const [isEditing, setIsEditing] = useState(false);
|
27
|
+
const [initialImageState, setInitialImageState] = useState([]);
|
31
28
|
|
32
29
|
const initialValues = {
|
33
30
|
place_images: PropertyToEdit?.place_images || [],
|
@@ -36,29 +33,55 @@ const Images = () => {
|
|
36
33
|
const formik = useFormik({
|
37
34
|
initialValues,
|
38
35
|
enableReinitialize: true,
|
39
|
-
validationSchema,
|
36
|
+
validationSchema: ImagevalidationSchema,
|
40
37
|
onSubmit: async (values, { setSubmitting }) => {
|
41
|
-
|
38
|
+
try {
|
39
|
+
setSubmitting(true);
|
42
40
|
|
43
|
-
|
44
|
-
|
41
|
+
if (slug && isEditing) {
|
42
|
+
const formData = new FormData();
|
43
|
+
formData.append("property[id]", slug);
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
45
|
+
// Append only new File objects to FormData
|
46
|
+
let hasNewFiles = false;
|
47
|
+
values.place_images.forEach((file) => {
|
48
|
+
if (file instanceof File) {
|
49
|
+
formData.append("property[place_images][]", file);
|
50
|
+
hasNewFiles = true;
|
51
|
+
}
|
52
|
+
});
|
53
|
+
if (hasNewFiles) {
|
54
|
+
const response = await dispatch(
|
55
|
+
uploadImageProperties(formData)
|
56
|
+
).unwrap();
|
57
|
+
if (response) {
|
58
|
+
successHandler("Images uploaded successfully");
|
59
|
+
navigate(`/property-3/${slug}`);
|
60
|
+
} else {
|
61
|
+
console.error("Upload failed:", response);
|
62
|
+
}
|
63
|
+
} else {
|
64
|
+
navigate(`/property-3/${slug}`);
|
65
|
+
}
|
66
|
+
} else if (slug && !isEditing) {
|
59
67
|
navigate(`/property-3/${slug}`);
|
60
68
|
} else {
|
61
|
-
|
69
|
+
const formData = new FormData();
|
70
|
+
values.place_images.forEach((file) => {
|
71
|
+
if (file instanceof File) {
|
72
|
+
formData.append("property[place_images][]", file);
|
73
|
+
}
|
74
|
+
});
|
75
|
+
|
76
|
+
const response = await dispatch(
|
77
|
+
uploadImageProperties(formData)
|
78
|
+
).unwrap();
|
79
|
+
if (response) {
|
80
|
+
successHandler("Images uploaded successfully");
|
81
|
+
navigate(`/property-3/${response?.property?.slug || slug}`);
|
82
|
+
} else {
|
83
|
+
console.error("Create failed:", response);
|
84
|
+
}
|
62
85
|
}
|
63
86
|
} catch (error) {
|
64
87
|
console.error("Error uploading images:", error);
|
@@ -68,29 +91,53 @@ const Images = () => {
|
|
68
91
|
},
|
69
92
|
});
|
70
93
|
|
71
|
-
|
72
|
-
|
94
|
+
// Track changes in images
|
73
95
|
useEffect(() => {
|
74
|
-
const
|
75
|
-
|
76
|
-
|
96
|
+
const currentImages = formik.values.place_images || [];
|
97
|
+
const hasChanges = !arraysEqual(currentImages, initialImageState);
|
98
|
+
setIsEditing(hasChanges);
|
99
|
+
}, [formik.values.place_images, initialImageState]);
|
100
|
+
|
101
|
+
// Helper function to compare arrays (considering File objects and URLs)
|
102
|
+
const arraysEqual = (a, b) => {
|
103
|
+
if (a.length !== b.length) return false;
|
104
|
+
|
105
|
+
for (let i = 0; i < a.length; i++) {
|
106
|
+
const itemA = a[i];
|
107
|
+
const itemB = b[i];
|
108
|
+
|
109
|
+
// Compare File objects by name and size
|
110
|
+
if (itemA instanceof File && itemB instanceof File) {
|
111
|
+
if (itemA.name !== itemB.name || itemA.size !== itemB.size) {
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
} else if (typeof itemA === "string" && typeof itemB === "string") {
|
115
|
+
// Compare URLs
|
116
|
+
if (itemA !== itemB) return false;
|
117
|
+
} else {
|
118
|
+
// Different types
|
119
|
+
return false;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
return true;
|
123
|
+
};
|
77
124
|
|
78
125
|
const onDrop = (acceptedFiles) => {
|
79
126
|
const newImages = [...formik.values.place_images, ...acceptedFiles];
|
80
127
|
formik.setFieldValue("place_images", newImages);
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
]);
|
128
|
+
|
129
|
+
const newPreviews = acceptedFiles.map((file) => URL.createObjectURL(file));
|
130
|
+
setPreviews((prev) => [...prev, ...newPreviews]);
|
85
131
|
};
|
86
132
|
|
87
133
|
const handleRemoveImage = (index) => {
|
88
134
|
const newImages = [...formik.values.place_images];
|
89
135
|
const newPreviews = [...previews];
|
90
|
-
|
136
|
+
if (newPreviews[index] && newPreviews[index].startsWith("blob:")) {
|
137
|
+
URL.revokeObjectURL(newPreviews[index]);
|
138
|
+
}
|
91
139
|
newImages.splice(index, 1);
|
92
140
|
newPreviews.splice(index, 1);
|
93
|
-
|
94
141
|
formik.setFieldValue("place_images", newImages);
|
95
142
|
setPreviews(newPreviews);
|
96
143
|
};
|
@@ -107,25 +154,53 @@ const Images = () => {
|
|
107
154
|
}, [dispatch, slug]);
|
108
155
|
|
109
156
|
useEffect(() => {
|
110
|
-
const
|
111
|
-
|
157
|
+
const existingImages = PropertyToEdit?.place_images || [];
|
158
|
+
const existingUrls = Array.isArray(existingImages)
|
159
|
+
? existingImages.filter((img) => typeof img === "string")
|
112
160
|
: [];
|
113
|
-
|
114
|
-
if (existingUrls.length > 0) {
|
161
|
+
if (existingImages.length > 0) {
|
115
162
|
setPreviews(existingUrls);
|
116
|
-
formik.setFieldValue("place_images",
|
163
|
+
formik.setFieldValue("place_images", existingImages);
|
164
|
+
setInitialImageState([...existingImages]); // Set initial state for comparison
|
165
|
+
} else {
|
166
|
+
// Reset states if no existing images
|
167
|
+
setPreviews([]);
|
168
|
+
formik.setFieldValue("place_images", []);
|
169
|
+
setInitialImageState([]);
|
117
170
|
}
|
118
171
|
}, [PropertyToEdit]);
|
119
172
|
|
120
|
-
|
121
|
-
|
173
|
+
// Handle form submission with validation
|
174
|
+
const handleSubmit = (e) => {
|
175
|
+
e.preventDefault();
|
176
|
+
formik.setTouched({
|
177
|
+
place_images: true,
|
178
|
+
});
|
179
|
+
|
180
|
+
if (formik.isValid) {
|
181
|
+
formik.handleSubmit(e);
|
182
|
+
} else {
|
183
|
+
console.log("Form validation failed, not submitting");
|
184
|
+
}
|
185
|
+
};
|
186
|
+
|
187
|
+
// Clean up blob URLs on unmount
|
188
|
+
useEffect(() => {
|
189
|
+
return () => {
|
190
|
+
previews.forEach((preview) => {
|
191
|
+
if (preview && preview.startsWith("blob:")) {
|
192
|
+
URL.revokeObjectURL(preview);
|
193
|
+
}
|
194
|
+
});
|
195
|
+
};
|
196
|
+
}, []);
|
122
197
|
|
123
198
|
return (
|
124
199
|
<CommonLayout currentHref="/property-2" PropertyID={routeId}>
|
125
200
|
<div className="image-step-container">
|
126
201
|
<h2 className="title">Upload Property Images</h2>
|
127
202
|
<div className="divider" />
|
128
|
-
<form className="image-form" onSubmit={
|
203
|
+
<form className="image-form" onSubmit={handleSubmit}>
|
129
204
|
<div className="form-group">
|
130
205
|
<label>Images</label>
|
131
206
|
<div {...getRootProps()} className="dropzone">
|
@@ -178,11 +253,13 @@ const Images = () => {
|
|
178
253
|
</div>
|
179
254
|
)}
|
180
255
|
<div className="actions">
|
181
|
-
<
|
256
|
+
<SecondryButton href={`/property-1/${slug}`}>
|
257
|
+
Go back
|
258
|
+
</SecondryButton>
|
182
259
|
<ButtonPrimary
|
183
260
|
type="submit"
|
184
261
|
className="submit-btn"
|
185
|
-
disabled={
|
262
|
+
disabled={formik.isSubmitting}
|
186
263
|
>
|
187
264
|
{formik.isSubmitting ? "Saving..." : "Save"}
|
188
265
|
</ButtonPrimary>
|
@@ -14,6 +14,8 @@ import FormField from "../../shared/FormField/FormField";
|
|
14
14
|
import ButtonPrimary from "../../shared/Button/ButtonPrimary";
|
15
15
|
import "../../styles/location.scss";
|
16
16
|
import successHandler from "../../utils/helpers/SuccessHandler";
|
17
|
+
import { LocationSchema } from "../../shared/Schema/FormSchema";
|
18
|
+
import SecondryButton from "../../shared/Button/SecondryButton";
|
17
19
|
|
18
20
|
const Location = () => {
|
19
21
|
const dispatch = useDispatch();
|
@@ -57,15 +59,23 @@ const Location = () => {
|
|
57
59
|
latitude: PropertyToEdit?.latitude || "",
|
58
60
|
longitude: PropertyToEdit?.longitude || "",
|
59
61
|
},
|
60
|
-
|
61
|
-
const errors = {};
|
62
|
-
if (!values.city) errors.city = "City is required";
|
63
|
-
if (!values.state) errors.state = "State is required";
|
64
|
-
if (!values.zipcode) errors.zipcode = "Zipcode is required";
|
65
|
-
return errors;
|
66
|
-
},
|
62
|
+
validationSchema: LocationSchema,
|
67
63
|
onSubmit: async (values) => {
|
64
|
+
const hasChanges = [
|
65
|
+
"city",
|
66
|
+
"state",
|
67
|
+
"zipcode",
|
68
|
+
"latitude",
|
69
|
+
"longitude",
|
70
|
+
].some((key) => values[key] !== PropertyToEdit?.[key]);
|
71
|
+
if (!hasChanges) {
|
72
|
+
navigate(`/property-5/${slug}`);
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
|
68
76
|
try {
|
77
|
+
setSubmitting(true);
|
78
|
+
|
69
79
|
const payload = {
|
70
80
|
id: slug,
|
71
81
|
city: values.city,
|
@@ -75,8 +85,6 @@ const Location = () => {
|
|
75
85
|
longitude: values.longitude,
|
76
86
|
};
|
77
87
|
|
78
|
-
formik.setSubmitting(true);
|
79
|
-
|
80
88
|
const response = await dispatch(
|
81
89
|
updateProperties({ property: payload })
|
82
90
|
).unwrap();
|
@@ -85,14 +93,13 @@ const Location = () => {
|
|
85
93
|
successHandler("Location updated successfully");
|
86
94
|
navigate(`/property-5/${slug}`);
|
87
95
|
} else {
|
88
|
-
console.error("Update failed:", response);
|
89
96
|
alert("Failed to update property. Please try again.");
|
90
97
|
}
|
91
98
|
} catch (error) {
|
92
99
|
console.error("Error updating property:", error);
|
93
100
|
alert("An error occurred while updating the property.");
|
94
101
|
} finally {
|
95
|
-
|
102
|
+
setSubmitting(false);
|
96
103
|
}
|
97
104
|
},
|
98
105
|
});
|
@@ -222,11 +229,11 @@ const Location = () => {
|
|
222
229
|
</div>
|
223
230
|
|
224
231
|
<div className="action-buttons">
|
225
|
-
<
|
232
|
+
<SecondryButton href={`/property-3/${slug}`}>
|
226
233
|
Go back
|
227
|
-
</
|
234
|
+
</SecondryButton>
|
228
235
|
<ButtonPrimary type="submit" disabled={isSubmitting}>
|
229
|
-
{isSubmitting ? "
|
236
|
+
{isSubmitting ? "Saving..." : "Save"}
|
230
237
|
</ButtonPrimary>
|
231
238
|
</div>
|
232
239
|
</div>
|