ISSUE 3: add image uploader and include add FAQ logic

This commit is contained in:
Ayobami
2025-06-30 22:17:18 +01:00
parent 6612336460
commit 05cd9c8318
+303 -150
View File
@@ -13,7 +13,12 @@ import MkdSDK from "@/utils/MkdSDK";
import useDelayUnmount from "@/hooks/useDelayUnmount"; import useDelayUnmount from "@/hooks/useDelayUnmount";
import { useContext } from "react"; import { useContext } from "react";
import { GlobalContext, showToast } from "@/globalContext"; import { GlobalContext, showToast } from "@/globalContext";
import { DRAFT_STATUS, IMAGE_STATUS, SPACE_STATUS, SPACE_VISIBILITY } from "@/utils/constants"; import {
DRAFT_STATUS,
IMAGE_STATUS,
SPACE_STATUS,
SPACE_VISIBILITY,
} from "@/utils/constants";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import CustomSelectV2 from "@/components/CustomSelectV2"; import CustomSelectV2 from "@/components/CustomSelectV2";
import useCancellation from "@/hooks/api/useCancellation"; import useCancellation from "@/hooks/api/useCancellation";
@@ -110,14 +115,30 @@ const SpaceDetailsTwo = () => {
for (let i = 0; i < pictures.length; i++) { for (let i = 0; i < pictures.length; i++) {
const file = pictures[i]; const file = pictures[i];
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml']; const allowedTypes = [
"image/jpeg",
"image/png",
"image/webp",
"image/svg+xml",
];
if (file?.type && !allowedTypes.includes(file?.type)) { if (file?.type && !allowedTypes.includes(file?.type)) {
showToast(globalDispatch, 'Invalid file type. Only JPEG, PNG, WEBP, and SVG are allowed.', 4000, "ERROR"); showToast(
globalDispatch,
"Invalid file type. Only JPEG, PNG, WEBP, and SVG are allowed.",
4000,
"ERROR"
);
return; return;
} }
if (file?.size && file?.size > 5 * 1024 * 1024) { // 5 MB limit if (file?.size && file?.size > 5 * 1024 * 1024) {
showToast(globalDispatch, 'One of the image is too large. Max size is 5 MB.', 4000, "ERROR"); // 5 MB limit
showToast(
globalDispatch,
"One or more of the image is too large. Max size is 5 MB.",
4000,
"ERROR"
);
return; return;
} }
@@ -128,7 +149,16 @@ const SpaceDetailsTwo = () => {
dispatch({ type: "SET_THUMBNAIL", payload: upload.id }); dispatch({ type: "SET_THUMBNAIL", payload: upload.id });
} }
} }
dispatch({ type: "SET_DETAILS_TWO", payload: { faqs: data.faqs, amenities: data.amenities, addons: data.addons, pictures: uploadedImages, pictureIds: uploadedIds } }); dispatch({
type: "SET_DETAILS_TWO",
payload: {
faqs: data.faqs,
amenities: data.amenities,
addons: data.addons,
pictures: uploadedImages,
pictureIds: uploadedIds,
},
});
globalDispatch({ type: "STOP_LOADING" }); globalDispatch({ type: "STOP_LOADING" });
navigate("/spaces/add/3"); navigate("/spaces/add/3");
@@ -156,7 +186,7 @@ const SpaceDetailsTwo = () => {
name: spaceData.name, name: spaceData.name,
rule: spaceData.rule, rule: spaceData.rule,
}, },
"POST", "POST"
); );
dispatch({ type: "SET_PROPERTY_ID", payload: propertyResult.message }); dispatch({ type: "SET_PROPERTY_ID", payload: propertyResult.message });
} }
@@ -177,7 +207,7 @@ const SpaceDetailsTwo = () => {
additional_guest_rate: spaceData.additional_guest_rate || undefined, additional_guest_rate: spaceData.additional_guest_rate || undefined,
size: spaceData.size || undefined, size: spaceData.size || undefined,
}, },
"POST", "POST"
); );
} }
@@ -190,7 +220,7 @@ const SpaceDetailsTwo = () => {
property_id: propertyResult?.message ?? spaceData.property_id, property_id: propertyResult?.message ?? spaceData.property_id,
add_on_id: addon_id, add_on_id: addon_id,
}, },
"POST", "POST"
); );
} }
@@ -208,7 +238,7 @@ const SpaceDetailsTwo = () => {
photo_id: upload.id, photo_id: upload.id,
is_approved: IMAGE_STATUS.IN_REVIEW, is_approved: IMAGE_STATUS.IN_REVIEW,
}, },
"POST", "POST"
); );
} }
if (file?.name == formValues.thumbnail) { if (file?.name == formValues.thumbnail) {
@@ -219,7 +249,7 @@ const SpaceDetailsTwo = () => {
default_image_id: upload.id, default_image_id: upload.id,
// is_approved: IMAGE_STATUS.APPROVED, // is_approved: IMAGE_STATUS.APPROVED,
}, },
"PUT", "PUT"
); );
} }
} }
@@ -234,7 +264,7 @@ const SpaceDetailsTwo = () => {
question: faq.question, question: faq.question,
answer: faq.answer, answer: faq.answer,
}, },
"POST", "POST"
); );
} }
@@ -247,7 +277,7 @@ const SpaceDetailsTwo = () => {
property_spaces_id: propertySpaceResult.message, property_spaces_id: propertySpaceResult.message,
amenity_id, amenity_id,
}, },
"POST", "POST"
); );
} }
navigate("/account/my-spaces"); navigate("/account/my-spaces");
@@ -278,138 +308,228 @@ const SpaceDetailsTwo = () => {
setPictures((prev) => { setPictures((prev) => {
var copy = [...prev]; var copy = [...prev];
copy[i] = picFile; copy[i] = picFile;
return copy; return copy.filter(Boolean);
}); });
}); });
} }
}, []); }, []);
function isCatOthers(){ function isCatOthers() {
const cat = spaceCategories.find((cat) => Number(cat.id) == Number(spaceData.category)) const cat = spaceCategories.find(
(cat) => Number(cat.id) == Number(spaceData.category)
);
if (cat?.category === "Others") { if (cat?.category === "Others") {
return true return true;
} else return false } else return false;
} }
return ( return (
<div className="min-h-screen pb-40 md:max-w-[656px]"> <div className='min-h-screen pb-40 md:max-w-[656px]'>
<form <form onSubmit={handleSubmit(onSubmit)} autoComplete='off'>
onSubmit={handleSubmit(onSubmit)} <h1 className='mb-8 text-3xl font-bold md:text-4xl'>Space Details</h1>
autoComplete="off" <div className='text-sm md:px-[20px] md:py-[32px]'>
> <h3 className='text-xl font-semibold md:text-2xl'>
<h1 className="mb-8 text-3xl font-bold md:text-4xl">Space Details</h1> * Photographs of the space
<div className="text-sm md:px-[20px] md:py-[32px]"> </h3>
<h3 className="text-xl font-semibold md:text-2xl">* Photographs of the space</h3> <p className='mb-8'>
<p className="mb-8">file type (jpeg/png/svg), max size (5MB), suggest resolution (640*480)</p> file type (jpeg/png/svg), max size (5MB), suggest resolution
<div className="eighteen-step-image mb-8 flex flex-wrap justify-center gap-x-2 gap-y-4 md:gap-5"> (640*480)
</p>
<div className='mb-8 flex flex-col justify-center gap-x-2 gap-y-4 md:gap-5'>
{/* FileUploader for images */}
<FileUploader
multiple
handleChange={(files) => {
const fileArray =
files instanceof FileList ? Array.from(files) : [files];
// Enforce max 6 images
if (pictures.length + fileArray.length > 6) {
showToast(
globalDispatch,
"You can only upload up to 6 images.",
4000,
"ERROR"
);
return;
}
// Enforce 1MB per image
for (let file of fileArray) {
if (file.size > 1024 * 1024) {
showToast(
globalDispatch,
"Each image must not exceed 1MB.",
4000,
"ERROR"
);
return;
}
}
setPictures((prev) => [...prev, ...fileArray]);
}}
name='images'
types={["JPG", "PNG", "JPEG", "SVG", "WEBP"]}
maxSize={1}
/>
{/* Show previews */}
<div className='eighteen-step-image flex flex-wrap gap-2'>
{pictures.map((file, idx) => { {pictures.map((file, idx) => {
// add FileUploader logic here const imageUrl =
file instanceof File ? URL.createObjectURL(file) : file;
return (
<div
key={idx}
className='relative flex flex-col items-center'
>
<img
src={imageUrl}
alt={`preview-${idx + 1}`}
className='mb-2 h-24 w-24 rounded border object-cover'
/>
<button
type='button'
className='text-xs text-red-500 underline'
onClick={() =>
setPictures((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
</div>
);
})} })}
</div> </div>
<h3 className="mb-4 text-xl font-bold">* Select thumbnail image</h3> </div>
<h3 className='mb-4 text-xl font-bold'>* Select thumbnail image</h3>
<CustomSelectV2 <CustomSelectV2
items={pictures.filter((pic) => pic?.name)} items={pictures.filter((pic) => pic?.name)}
labelField="name" labelField='name'
valueField="name" valueField='name'
containerClassName="mb-12" containerClassName='mb-12'
className="w-full border py-2 px-3 focus:outline-primary" className='w-full border px-3 py-2 focus:outline-primary'
openClassName="ring-primary ring-2" openClassName='ring-primary ring-2'
placeholder={"Select thumbnail"} placeholder={"Select thumbnail"}
control={control} control={control}
name="thumbnail" name='thumbnail'
/> />
<h3 className="mb-4 text-xl font-bold"> <h3 className='mb-4 text-xl font-bold'>
What do you offer with the space <span className="text-sm font-normal italic text-gray-500">(optional)</span> What do you offer with the space{" "}
<span className='text-sm font-normal italic text-gray-500'>
(optional)
</span>
</h3> </h3>
<div className="flex gap-3 items-center"> <div className='flex items-center gap-3'>
<button <button
type="button" type='button'
className="mb-2 font-bold text-[#1570EF]" className='mb-2 font-bold text-[#1570EF]'
onClick={() => setAddAmenitiesPopup(true)} onClick={() => setAddAmenitiesPopup(true)}
> >
+ Select items + Select items
</button> </button>
</div> </div>
<div className="addons-grid mb-12"> <div className='addons-grid mb-12'>
{amenities {amenities
?.filter((am) => { ?.filter((am) => {
if (Array.isArray(selectedAmenities)) { if (Array.isArray(selectedAmenities)) {
return selectedAmenities?.includes(String(am.id)); return selectedAmenities?.includes(String(am.id));
} }
return false; return false;
}).sort((a, b) => (a.space_id === null ? -1 : 1) - (b.space_id === null ? -1 : 1)) })
.sort(
(a, b) =>
(a.space_id === null ? -1 : 1) -
(b.space_id === null ? -1 : 1)
)
.map((am) => ( .map((am) => (
<li <li
className="flex w-fit items-center gap-2 mb-4 sm:mb-0" className='mb-4 flex w-fit items-center gap-2 sm:mb-0'
key={am.id}> key={am.id}
>
<CircleCheckIcon /> <CircleCheckIcon />
{am.name} {am.name}
</li> </li>
))} ))}
</div> </div>
<h3 className="mb-4 text-xl font-bold"> <h3 className='mb-4 text-xl font-bold'>
Add-ons <span className="text-sm font-normal italic text-gray-500">(optional)</span> Add-ons{" "}
<span className='text-sm font-normal italic text-gray-500'>
(optional)
</span>
</h3> </h3>
<div className="flex gap-3 items-center"> <div className='flex items-center gap-3'>
<button <button
type="button" type='button'
className="mb-2 font-bold text-[#1570EF]" className='mb-2 font-bold text-[#1570EF]'
onClick={() => setAddAddonsPopup(true)} onClick={() => setAddAddonsPopup(true)}
> >
+ Select items + Select items
</button> </button>
</div> </div>
<div className="addons-grid mb-12"> <div className='addons-grid mb-12'>
{addons {addons
?.filter((addon) => { ?.filter((addon) => {
if (Array.isArray(selectedAddons)) { if (Array.isArray(selectedAddons)) {
return selectedAddons?.includes(String(addon.id)); return selectedAddons?.includes(String(addon.id));
} }
return false; return false;
}).sort((a, b) => (a.space_id === null ? -1 : 1) - (b.space_id === null ? -1 : 1)) })
.sort(
(a, b) =>
(a.space_id === null ? -1 : 1) -
(b.space_id === null ? -1 : 1)
)
.map((addon) => ( .map((addon) => (
<li <li
className="flex w-fit items-center gap-2 mb-4 sm:mb-0" className='mb-4 flex w-fit items-center gap-2 sm:mb-0'
key={addon.id}> key={addon.id}
>
<CircleCheckIcon /> <CircleCheckIcon />
{addon.name}</li> {addon.name}
</li>
))} ))}
</div> </div>
<h3 className="mb-2 text-xl font-bold"> <h3 className='mb-2 text-xl font-bold'>
Frequently asked question <span className="text-sm font-normal italic text-gray-500">(optional)</span> Frequently asked question{" "}
<span className='text-sm font-normal italic text-gray-500'>
(optional)
</span>
</h3> </h3>
<p>These FAQs will show as part of your space listing.</p> <p>These FAQs will show as part of your space listing.</p>
<div> <div>
{fields.map((field, index) => ( {fields.map((field, index) => (
<div <div className='p-[20px]' key={field.id}>
className="p-[20px]" <div className='flex justify-between'>
key={field.id} <label className='mb-1 font-semibold'>
> * Question #{index + 1}
<div className="flex justify-between"> </label>
<label className="mb-1 font-semibold">* Question #{index + 1}</label>
<button <button
className="text-sm font-semibold text-[#667085]" className='text-sm font-semibold text-[#667085]'
onClick={() => remove(index)} onClick={() => remove(index)}
> >
Delete Delete
</button> </button>
</div> </div>
<input <input
placeholder="" placeholder=''
autoComplete="off" autoComplete='off'
{...register(`faqs.${index}.question`)} {...register(`faqs.${index}.question`)}
className={`mb-4 w-full rounded border py-2 px-3 leading-tight text-gray-700 focus:outline-primary`} className={`mb-4 w-full rounded border px-3 py-2 leading-tight text-gray-700 focus:outline-primary`}
/> />
<br /> <br />
<label className="mb-2 font-semibold">* Answer #{index + 1}</label> <label className='mb-2 font-semibold'>
* Answer #{index + 1}
</label>
<SunEditor <SunEditor
width="100%" width='100%'
height="107px" height='107px'
onChange={(content) => setValue(`faqs.${index}.answer`, content)} onChange={(content) =>
placeholder="" setValue(`faqs.${index}.answer`, content)
}
placeholder=''
hideToolbar={true} hideToolbar={true}
setOptions={{ resizingBar: false }} setOptions={{ resizingBar: false }}
defaultValue={getValues().faqs[index].answer} defaultValue={getValues().faqs[index].answer}
@@ -418,62 +538,76 @@ const SpaceDetailsTwo = () => {
))} ))}
<button <button
className="mb-12 font-bold text-[#1570EF]" className='mb-12 font-bold text-[#1570EF]'
type="button" type='button'
id="append_faq_btn" id='append_faq_btn'
// add logic to handle appending new FAQs field onClick={() => append({ question: "", answer: "" })}
> >
+ Add question + Add question
</button> </button>
</div> </div>
</div> </div>
<hr className="my-[20px]" /> <hr className='my-[20px]' />
<p className="px-4 text-sm md:px-0" dangerouslySetInnerHTML={{ __html: sanitizeAndTruncate(cancellationPolicy, 400) }}> <p
</p> className='px-4 text-sm md:px-0'
<div className="flex justify-end"> dangerouslySetInnerHTML={{
<Link to={"/help/cancellation-policy"} __html: sanitizeAndTruncate(cancellationPolicy, 400),
className="mt-4 text-end text-sm font-semibold underline" }}
></p>
<div className='flex justify-end'>
<Link
to={"/help/cancellation-policy"}
className='mt-4 text-end text-sm font-semibold underline'
target={"_blank"} target={"_blank"}
> >
View More View More
</Link> </Link>
</div> </div>
<hr className="my-[30px]" /> <hr className='my-[30px]' />
<button <button
type="submit" type='submit'
className="login-btn-gradient rounded py-2 px-4 tracking-wide text-white outline-none focus:outline-none" className='login-btn-gradient rounded px-4 py-2 tracking-wide text-white outline-none focus:outline-none'
> >
Continue Continue
</button> </button>
<br /> <br />
<div <div
className={`${showAddAmenitiesPopup ? "flex" : "hidden"} popup-container items-center justify-center normal-case`} className={`${
showAddAmenitiesPopup ? "flex" : "hidden"
} popup-container items-center justify-center normal-case`}
onClick={() => setAddAmenitiesPopup(false)} onClick={() => setAddAmenitiesPopup(false)}
> >
<div <div
className={`${addAmenitiesPopup ? "pop-in" : "pop-out"} w-[510px] max-w-[80%] rounded-lg bg-white p-5 px-3 md:px-5`} className={`${
addAmenitiesPopup ? "pop-in" : "pop-out"
} w-[510px] max-w-[80%] rounded-lg bg-white p-5 px-3 md:px-5`}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<div className="mb-[18px] flex items-center justify-between"> <div className='mb-[18px] flex items-center justify-between'>
<h3 className="mb-[8px] text-2xl font-semibold">Select Amenities</h3> <h3 className='mb-[8px] text-2xl font-semibold'>
Select Amenities
</h3>
<button <button
type="button" type='button'
onClick={() => setAddAmenitiesPopup(false)} onClick={() => setAddAmenitiesPopup(false)}
className="rounded-full border p-1 px-3 text-2xl font-normal duration-100 hover:bg-gray-200 active:bg-gray-300" className='rounded-full border p-1 px-3 text-2xl font-normal duration-100 hover:bg-gray-200 active:bg-gray-300'
> >
&#x2715; &#x2715;
</button> </button>
</div> </div>
<div className="review-scroll max-h-[400px] overflow-y-auto"> <div className='review-scroll max-h-[400px] overflow-y-auto'>
{isCatOthers() ? {isCatOthers()
amenities.sort((a, b) => (a.creator_id !== 1 ? -1 : 1) - (b.creator_id !== 1 ? -1 : 1)).map((am) => ( ? amenities
<div .sort(
key={am.id} (a, b) =>
className="checkbox-container mb-4" (a.creator_id !== 1 ? -1 : 1) -
> (b.creator_id !== 1 ? -1 : 1)
)
.map((am) => (
<div key={am.id} className='checkbox-container mb-4'>
<input <input
type="checkbox" type='checkbox'
className="" className=''
{...register("amenities")} {...register("amenities")}
id={"amenity" + am.id} id={"amenity" + am.id}
value={am.id} value={am.id}
@@ -481,94 +615,113 @@ const SpaceDetailsTwo = () => {
<label htmlFor={"amenity" + am.id}>{am.name}</label> <label htmlFor={"amenity" + am.id}>{am.name}</label>
</div> </div>
)) ))
: : amenities
amenities.filter((am) => (am.space_id === Number(spaceData.category)) || am.creator_id === Number(localStorage.getItem("user"))).sort((a, b) => (a.creator_id !== 1 ? -1 : 1) - (b.creator_id !== 1 ? -1 : 1)).map((am) => ( .filter(
<div (am) =>
key={am.id} am.space_id === Number(spaceData.category) ||
className="checkbox-container mb-4" am.creator_id === Number(localStorage.getItem("user"))
> )
.sort(
(a, b) =>
(a.creator_id !== 1 ? -1 : 1) -
(b.creator_id !== 1 ? -1 : 1)
)
.map((am) => (
<div key={am.id} className='checkbox-container mb-4'>
<input <input
type="checkbox" type='checkbox'
className="" className=''
{...register("amenities")} {...register("amenities")}
id={"amenity" + am.id} id={"amenity" + am.id}
value={am.id} value={am.id}
/> />
<label htmlFor={"amenity" + am.id}>{am.name}</label> <label htmlFor={"amenity" + am.id}>{am.name}</label>
</div> </div>
)) ))}
}
</div> </div>
</div> </div>
</div> </div>
<div <div
className={`${showAddAddonsPopup ? "flex" : "hidden"} popup-container items-center justify-center normal-case`} className={`${
showAddAddonsPopup ? "flex" : "hidden"
} popup-container items-center justify-center normal-case`}
onClick={() => setAddAddonsPopup(false)} onClick={() => setAddAddonsPopup(false)}
> >
<div <div
className={`${addAddonsPopup ? "pop-in" : "pop-out"} w-[510px] max-w-[80%] rounded-lg bg-white p-5 px-3 md:px-5`} className={`${
addAddonsPopup ? "pop-in" : "pop-out"
} w-[510px] max-w-[80%] rounded-lg bg-white p-5 px-3 md:px-5`}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<div className="mb-[18px] flex items-center justify-between"> <div className='mb-[18px] flex items-center justify-between'>
<h3 className="mb-[8px] text-2xl font-semibold">Select Addons</h3> <h3 className='mb-[8px] text-2xl font-semibold'>Select Addons</h3>
<button <button
type="button" type='button'
onClick={() => setAddAddonsPopup(false)} onClick={() => setAddAddonsPopup(false)}
className="rounded-full border p-1 px-3 text-2xl font-normal duration-100 hover:bg-gray-200 active:bg-gray-300" className='rounded-full border p-1 px-3 text-2xl font-normal duration-100 hover:bg-gray-200 active:bg-gray-300'
> >
&#x2715; &#x2715;
</button> </button>
</div> </div>
<div className="review-scroll max-h-[400px] overflow-y-auto"> <div className='review-scroll max-h-[400px] overflow-y-auto'>
{isCatOthers() ? {isCatOthers()
addons.sort((a, b) => (a.creator_id !== 1 ? -1 : 1) - (b.creator_id !== 1 ? -1 : 1)).map((addon) => ( ? addons
<div .sort(
key={addon.id} (a, b) =>
className="checkbox-container mb-4" (a.creator_id !== 1 ? -1 : 1) -
> (b.creator_id !== 1 ? -1 : 1)
<input )
type="checkbox"
{...register("addons")}
id={"addon" + addon.id}
value={addon.id}
/>
<label htmlFor={"addon" + addon.id}>{addon.name}{" "}${addon.cost}</label>
</div>
))
:
addons.sort((a, b) => (a.creator_id !== 1 ? -1 : 1) - (b.creator_id !== 1 ? -1 : 1)).filter((ad) => ad.space_id === Number(spaceData.category) || ad.creator_id === Number(localStorage.getItem("user")))
.map((addon) => ( .map((addon) => (
<div <div key={addon.id} className='checkbox-container mb-4'>
key={addon.id}
className="checkbox-container mb-4"
>
<input <input
type="checkbox" type='checkbox'
{...register("addons")} {...register("addons")}
id={"addon" + addon.id} id={"addon" + addon.id}
value={addon.id} value={addon.id}
/> />
<label htmlFor={"addon" + addon.id}>{addon.name}{" "}${addon.cost}</label> <label htmlFor={"addon" + addon.id}>
{addon.name} ${addon.cost}
</label>
</div> </div>
)) ))
: addons
} .sort(
(a, b) =>
(a.creator_id !== 1 ? -1 : 1) -
(b.creator_id !== 1 ? -1 : 1)
)
.filter(
(ad) =>
ad.space_id === Number(spaceData.category) ||
ad.creator_id === Number(localStorage.getItem("user"))
)
.map((addon) => (
<div key={addon.id} className='checkbox-container mb-4'>
<input
type='checkbox'
{...register("addons")}
id={"addon" + addon.id}
value={addon.id}
/>
<label htmlFor={"addon" + addon.id}>
{addon.name} ${addon.cost}
</label>
</div>
))}
</div> </div>
</div> </div>
</div> </div>
<button <button
type="button" type='button'
id="save-as-draft" id='save-as-draft'
className="mt-[24px] rounded border-2 border-[#98A2B3] py-2 px-4 tracking-wide outline-none focus:outline-none" className='mt-[24px] rounded border-2 border-[#98A2B3] px-4 py-2 tracking-wide outline-none focus:outline-none'
onClick={() => onSaveDraft()} onClick={() => onSaveDraft()}
> >
Save draft and exit Save draft and exit
</button> </button>
</form> </form>
{addOnModal && {addOnModal && <HostAddAddonsModal setAddOnModal={setAddOnModal} />}
<HostAddAddonsModal setAddOnModal={setAddOnModal}/>
}
</div> </div>
); );
}; };