import React, { useEffect, useState } from "react"; import PropertySpaceCard from "@/components/frontend/PropertySpaceCard"; import { useForm } from "react-hook-form"; import { useSearchParams } from "react-router-dom"; import InfiniteScroll from "react-infinite-scroll-component"; import NoteIcon from "@/components/frontend/icons/NoteIcon"; import { isValidDate, parseSearchParams } from "@/utils/utils"; import { useContext } from "react"; import { GlobalContext } from "@/globalContext"; import HostCardSlider from "@/components/frontend/HostCardSlider"; import CustomSelectV2 from "@/components/CustomSelectV2"; import CustomLocationAutoCompleteV2 from "@/components/CustomLocationAutoCompleteV2"; import DatePickerV3 from "@/components/DatePickerV3"; import { DRAFT_STATUS, SPACE_STATUS, SPACE_VISIBILITY } from "@/utils/constants"; import { AuthContext, tokenExpireError } from "@/authContext"; import MkdSDK from "@/utils/MkdSDK"; import PropertySpaceFiltersModal from "@/components/PropertySpaceFiltersModal"; import { AdjustmentsHorizontalIcon } from "@heroicons/react/24/solid"; const prices = [ { label: "All Prices", value: "", }, { label: "$0 - $30", value: "$0 - $30", }, { label: "$31 - $60", value: "$31 - $60", }, { label: "$60 - $90", value: "$60 - $90", }, { label: "$90 - $120", value: "$90 - $120", }, { label: "$120 - $150", value: "$120 - $150", }, { label: "$150 - $180", value: "$150 - $180", }, ]; const sdk = new MkdSDK(); const ExplorePage = () => { const FETCH_PER_SCROLL = 12; const [searchParams, setSearchParams] = useSearchParams(); const section = searchParams.get("section") ?? "all"; const [hosts, setHosts] = useState([]); const [popularSpaces, setPopularSpaces] = useState([]); const [newSpaces, setNewSpaces] = useState([]); const [showFilter, setShowFilter] = useState(false); const [forceRender, setForceRender] = useState(""); const { dispatch: globalDispatch, state: globalState } = useContext(GlobalContext); const { dispatch } = useContext(AuthContext); const [ctrl] = useState(new AbortController()); const { handleSubmit, register, watch, reset, setValue, control, formState, resetField } = useForm({ defaultValues: (() => { const params = parseSearchParams(searchParams); return { location: params.location ?? "", from: isValidDate(params.from ?? "") ? new Date(params.from) : new Date(), to: isValidDate(params.to ?? "") ? new Date(params.to) : new Date(), space_name: params.space_name ?? "", category: params.category ?? "", price_range: params.price_range ?? "", direction: "DESC", }; })(), }); const { dirtyFields } = formState; const direction = watch("direction"); const fromDate = watch("from"); const [popularTotal, setPopularTotal] = useState(10000); const [newSpaceTotal, setNewSpaceTotal] = useState(10000); async function fetchPopularSpaces(page) { setPopularSpaces([]); setPopularSpaces((prev) => { const amountToFetch = popularTotal - prev.length > FETCH_PER_SCROLL ? FETCH_PER_SCROLL : Math.abs(popularTotal - prev.length - FETCH_PER_SCROLL); return [...prev, ...Array(amountToFetch).fill({})]; }); const data = parseSearchParams(searchParams); const user_id = localStorage.getItem("user"); const location = (data.location?.split(",")) var from_price, to_price; if (data.price_range) { var arr = data.price_range.split("-"); if (arr.length > 1) { from_price = arr[0].trim().slice(1); to_price = arr[1].trim().slice(1); } } let where = [ `ergo_property_spaces.space_status = ${SPACE_STATUS.APPROVED} AND ergo_property_spaces_images.is_approved = 1 AND schedule_template_id IS NOT NULL AND ergo_property_spaces.draft_status = ${DRAFT_STATUS.COMPLETED} AND ergo_property_spaces.deleted_at IS NULL`, ]; if (data.category) { where.push(`ergo_spaces.category = '${data.category}'`); } if (data.space_name) { where.push(`ergo_property.name LIKE '%${data.space_name}%'`); } if (data.price_range) { where.push(`ergo_property_spaces.rate BETWEEN ${from_price} AND ${to_price}`); } if (data.location) { where.push( `(ergo_property.address_line_1 LIKE '%${data.location}%' OR ergo_property.address_line_2 LIKE '%${data.location}%' OR ergo_property.city LIKE '%${location[0]}%' OR ergo_property.country LIKE '%${location.length === 1 ? location[0] : location.length === 2 ? location[1] : location[2]}%' OR ergo_property.zip LIKE '%${data.location}%' OR ergo_property.name LIKE '%${data.location}%')`, ); } try { const result = await sdk.callRawAPI( "/v2/api/custom/ergo/popular/PAGINATE", { page: page ?? 1, limit: FETCH_PER_SCROLL, user_id: Number(user_id), where, booking_start_time: isValidDate(data.from || "") ? new Date(data.from).toISOString() : undefined, booking_end_time: isValidDate(data.to || "") ? new Date(data.to).toISOString() : undefined, sortId: direction == "NONE" ? undefined : "id", direction: direction == "NONE" ? undefined : direction, }, "POST", ctrl.signal, ); if (Array.isArray(result.list)) { setPopularSpaces((prev) => { return [...prev.filter((item) => Object.keys(item).length > 0), ...result.list].filter((v, i, a) => a.findIndex((v2) => v2.id === v.id) === i); }); setPopularTotal(result.total); } } catch (err) { tokenExpireError(dispatch, err.message); if (err.name == "AbortError") return; globalDispatch({ type: "SHOW_ERROR", payload: { heading: "Operation failed", message: err.message, }, }); } } async function fetchNewSpaces(page) { setNewSpaces([]); setNewSpaces((prev) => { const amountToFetch = newSpaceTotal - prev.length > FETCH_PER_SCROLL ? FETCH_PER_SCROLL : Math.abs(newSpaceTotal - prev.length - FETCH_PER_SCROLL); return [...prev, ...Array(amountToFetch).fill({})]; }); const data = parseSearchParams(searchParams); const user_id = localStorage.getItem("user"); var from_price, to_price; if (data.price_range) { var arr = data.price_range.split("-"); if (arr.length > 1) { from_price = arr[0].trim().slice(1); to_price = arr[1].trim().slice(1); } } let where = [ `ergo_property_spaces.space_status = ${SPACE_STATUS.APPROVED} AND ergo_property_spaces.draft_status = ${DRAFT_STATUS.COMPLETED} AND ergo_property_spaces.availability = ${SPACE_VISIBILITY.VISIBLE} AND ergo_property_spaces_images.is_approved = 1`, ]; if (data.category) { where.push(`ergo_spaces.category = '${data.category}'`); } if (data.space_name) { where.push(`ergo_property.name LIKE '%${data.space_name}%'`); } if (data.price_range) { where.push(`ergo_property_spaces.rate BETWEEN ${from_price} AND ${to_price}`); } if (data.location) { where.push( `(ergo_property.address_line_1 LIKE '%${location}%' OR ergo_property.address_line_2 LIKE '%${location}%' OR ergo_property.city LIKE '%${location[0] ?? ""}%' OR ergo_property.country LIKE '%${location.length === 1 ? location[0] : location.length === 2 ? location[1] : location[2]}%' OR ergo_property.zip LIKE '%${location}%' OR ergo_property.name LIKE '%${location}%')`, ); } try { const result = await sdk.callRawAPI( "/v2/api/custom/ergo/popular/PAGINATE", { page: page ?? 1, limit: FETCH_PER_SCROLL, user_id: Number(user_id), where, sortId: "update_at", direction: "DESC", booking_start_time: isValidDate(data.from || "") ? new Date(data.from).toISOString() : undefined, booking_end_time: isValidDate(data.to || "") ? new Date(data.to).toISOString() : undefined, }, "POST", ctrl.signal, ); if (Array.isArray(result.list)) { setNewSpaces((prev) => { return [...prev.filter((item) => Object.keys(item).length > 0), ...result.list].filter((v, i, a) => a.findIndex((v2) => v2.id === v.id) === i); }); setNewSpaceTotal(result.total); } } catch (err) { tokenExpireError(dispatch, err.message); if (err.name == "AbortError") return; globalDispatch({ type: "SHOW_ERROR", payload: { heading: "Operation failed", message: err.message, }, }); } } async function fetchHosts() { const filter = parseSearchParams(searchParams); const data = parseSearchParams(searchParams); const location = (data.location?.replace(', undefined', '')?.split(",")) const user_id = localStorage.getItem("user"); var from_price, to_price; if (data.price_range) { var arr = data.price_range.split("-"); if (arr.length > 1) { from_price = arr[0].trim().slice(1); to_price = arr[1].trim().slice(1); } } let where = []; where.push('ergo_property.id IS NOT NULL'); if (data.category) { where.push(`ergo_spaces.category = '${data.category}'`); } if (data.space_name) { where.push(`ergo_property.name LIKE '%${data.space_name}%'`); } if (data.from) { where.push(`ergo_user.create_at BETWEEN '${data.from}' AND '${data.to}'`); } if (data.price_range) { where.push(`ergo_property_spaces.rate BETWEEN ${from_price} AND ${to_price}`); } if (data.location) { where.push([ `(ergo_profile.address_line_1 LIKE '%${data.location}%' OR ergo_profile.address_line_2 LIKE '%${data.location}%' OR ergo_profile.city LIKE '%${location[0]}%' OR ergo_profile.country LIKE '%${location[1]}%' OR ergo_profile.zip LIKE '%${data.location}%')`, ]); } try { const result = await sdk.callRawAPI("/v2/api/custom/ergo/top-hosts/PAGINATE", { page: 1, limit: 1000, sortId: "avg_host_rating", direction: "DESC", where, booking_start_time: isValidDate(data.from || "") ? new Date(data.from).toISOString() : undefined, booking_end_time: isValidDate(data.to || "") ? new Date(data.to).toISOString() : undefined, }, "POST", ctrl.signal); setHosts(result.list); } catch (err) { tokenExpireError(dispatch, err.message); if (err.name == "AbortError") return; globalDispatch({ type: "SHOW_ERROR", payload: { heading: "Operation failed", message: err.message, }, }); } } useEffect(() => { switch (searchParams.get("section")) { case "popular": fetchPopularSpaces(); break; case "hosts": fetchHosts(); break; case "new-spaces": fetchNewSpaces(); break; default: fetchHosts(); fetchPopularSpaces(); fetchNewSpaces(); } }, [searchParams]); useEffect(() => { if (forceRender) { setPopularSpaces([]); setNewSpaces([]); fetchPopularSpaces(); fetchNewSpaces(); } }, [forceRender]); useEffect(() => { return () => { // TODO: abort this only when component unmounts // console.log("aborting"); // ctrl.abort(); }; }, []); const onSubmit = async (data) => { if (window.innerWidth < 700) { setShowFilter(false); } if (data.location.includes("undefined")) { const parts = inputString.split(","); const result = parts[0].trim(); data.location = result; } searchParams.set("category", data.category); searchParams.set("price_range", data.price_range); searchParams.set("space_name", data.space_name); searchParams.set("location", data.location); searchParams.set("from", dirtyFields?.from ? data.from.toISOString() : ""); searchParams.set("to", dirtyFields?.to ? data.to.toISOString() : ""); setSearchParams(searchParams); }; const sortByDate = (a, b) => { if (direction == "NONE") return 0; if (direction == "DESC") { return new Date(b.id) - new Date(a.id); } return new Date(a.id) - new Date(b.id); }; return (
} > {
} > {