Files
web/components/PagesComponent/Home/HomeHeader.jsx
Husanjonazamov 64af77101f classify web
2026-02-24 12:52:49 +05:00

436 lines
14 KiB
JavaScript

"use client";
import LanguageDropdown from "@/components/Common/LanguageDropdown";
import { CurrentLanguageData } from "@/redux/reducer/languageSlice";
import {
getIsFreAdListing,
getOtpServiceProvider,
settingsData,
} from "@/redux/reducer/settingSlice";
import { t, truncate } from "@/utils";
import CustomLink from "@/components/Common/CustomLink";
import { useSelector } from "react-redux";
import { GrLocation } from "react-icons/gr";
import { getCityData } from "@/redux/reducer/locationSlice";
import HomeMobileMenu from "./HomeMobileMenu.jsx";
import MailSentSuccessModal from "@/components/Auth/MailSentSuccessModal.jsx";
import { useEffect, useState } from "react";
import {
getIsLoggedIn,
logoutSuccess,
userSignUpData,
} from "@/redux/reducer/authSlice.js";
import ProfileDropdown from "./ProfileDropdown.jsx";
import { toast } from "sonner";
import FirebaseData from "@/utils/Firebase.js";
import { IoIosAddCircleOutline } from "react-icons/io";
import dynamic from "next/dynamic";
import {
getIsLoginModalOpen,
setIsLoginOpen,
} from "@/redux/reducer/globalStateSlice.js";
import ReusableAlertDialog from "@/components/Common/ReusableAlertDialog";
import { deleteUserApi, getLimitsApi, logoutApi } from "@/utils/api.js";
import { useMediaQuery } from "usehooks-ts";
import UnauthorizedModal from "@/components/Auth/UnauthorizedModal.jsx";
import CustomImage from "@/components/Common/CustomImage.jsx";
import { Loader2 } from "lucide-react";
import { useNavigate } from "@/components/Common/useNavigate.jsx";
import { usePathname } from "next/navigation.js";
import { Skeleton } from "@/components/ui/skeleton.jsx";
import HeaderCategories from "./HeaderCategories.jsx";
import { deleteUser, getAuth } from "firebase/auth";
import DeleteAccountVerifyOtpModal from "@/components/Auth/DeleteAccountVerifyOtpModal.jsx";
import useGetCategories from "@/components/Layout/useGetCategories.jsx";
const Search = dynamic(() => import("./Search.jsx"), {
ssr: false,
});
const LoginModal = dynamic(() => import("@/components/Auth/LoginModal.jsx"), {
ssr: false,
});
const RegisterModal = dynamic(
() => import("@/components/Auth/RegisterModal.jsx"),
{
ssr: false,
}
);
const LocationModal = dynamic(
() => import("@/components/Location/LocationModal.jsx"),
{
ssr: false,
}
);
const HeaderCategoriesSkeleton = () => {
return (
<div className="container">
<div className="py-1.5 border-b">
<Skeleton className="w-full h-[40px]" />
</div>
</div>
);
};
const HomeHeader = () => {
// 📦 Framework & Firebase
const { navigate } = useNavigate();
const { signOut } = FirebaseData();
const pathname = usePathname();
// 🔌 Redux State (via useSelector)
// User & Auth
const userData = useSelector(userSignUpData);
const IsLoggedin = useSelector(getIsLoggedIn);
const IsLoginOpen = useSelector(getIsLoginModalOpen);
const otp_service_provider = useSelector(getOtpServiceProvider);
// Ads & Categories
// const isCategoryLoading = useSelector(getIsCatLoading);
// const cateData = useSelector(CategoryData);
const { getCategories, cateData, isCatLoading: isCategoryLoading } = useGetCategories();
const IsFreeAdListing = useSelector(getIsFreAdListing);
// Location
const cityData = useSelector(getCityData);
// Language & Settings
const CurrentLanguage = useSelector(CurrentLanguageData);
const settings = useSelector(settingsData);
// 🎛️ Local UI State (via useState)
// Modals
const [IsRegisterModalOpen, setIsRegisterModalOpen] = useState(false);
const [IsLocationModalOpen, setIsLocationModalOpen] = useState(false);
const [IsVerifyOtpBeforeDelete, setIsVerifyOtpBeforeDelete] = useState(false);
// Auth State
const [IsLogout, setIsLogout] = useState(false);
const [IsLoggingOut, setIsLoggingOut] = useState(false);
// Profile
const [IsUpdatingProfile, setIsUpdatingProfile] = useState(false);
// Ad Listing
const [IsAdListingClicked, setIsAdListingClicked] = useState(false);
// Email Status
const [IsMailSentSuccess, setIsMailSentSuccess] = useState(false);
// 📱 Media Query
const isLargeScreen = useMediaQuery("(min-width: 992px)");
//delete account state
const [manageDeleteAccount, setManageDeleteAccount] = useState({
IsDeleteAccount: false,
IsDeleting: false,
});
useEffect(() => {
getCategories(1);
}, [CurrentLanguage.id]);
const handleLogout = async () => {
try {
setIsLoggingOut(true);
await signOut();
const res = await logoutApi.logoutApi({
...(userData?.fcm_id && { fcm_token: userData?.fcm_id }),
});
if (res?.data?.error === false) {
logoutSuccess();
toast.success(t("signOutSuccess"));
setIsLogout(false);
// avoid redirect if already on home page otherwise router.push triggering server side api calls
if (pathname !== "/") {
navigate("/");
}
} else {
toast.error(res?.data?.message);
}
} catch (error) {
console.log("Failed to log out", error);
} finally {
setIsLoggingOut(false);
}
};
const handleAdListing = async () => {
if (!IsLoggedin) {
setIsLoginOpen(true);
return;
}
if (!userData?.name || !userData?.email) {
setIsUpdatingProfile(true);
return;
}
if (IsFreeAdListing) {
navigate("/ad-listing");
return;
}
try {
setIsAdListingClicked(true);
const res = await getLimitsApi.getLimits({
package_type: "item_listing",
});
if (res?.data?.error === false) {
navigate("/ad-listing");
} else {
toast.error(t("purchasePlan"));
navigate("/subscription");
}
} catch (error) {
console.log(error);
} finally {
setIsAdListingClicked(false);
}
};
const handleUpdateProfile = () => {
setIsUpdatingProfile(false);
navigate("/profile");
};
const locationText =
cityData?.address_translated || cityData?.formattedAddress;
const handleDeleteAcc = async () => {
try {
setManageDeleteAccount((prev) => ({ ...prev, IsDeleting: true }));
const auth = getAuth();
const user = auth.currentUser;
const isMobileLogin = userData?.type == "phone";
const needsOtpVerification = isMobileLogin && !user && otp_service_provider === "firebase";
if (user) {
await deleteUser(user);
} else if (needsOtpVerification) {
setManageDeleteAccount((prev) => ({ ...prev, IsDeleteAccount: false }));
setIsVerifyOtpBeforeDelete(true);
return;
}
await deleteUserApi.deleteUser();
logoutSuccess();
toast.success(t("userDeleteSuccess"));
setManageDeleteAccount((prev) => ({ ...prev, IsDeleteAccount: false }));
// avoid redirect if already on home page otherwise router.push triggering server side api calls
if (pathname !== "/") {
navigate("/");
}
} catch (error) {
console.error("Error deleting user:", error.message);
const isMobileLogin = userData?.type === "phone";
if (error.code === "auth/requires-recent-login") {
if (isMobileLogin) {
setManageDeleteAccount((prev) => ({
...prev,
IsDeleteAccount: false, // close delete modal
}));
setIsVerifyOtpBeforeDelete(true); // open OTP screen
return;
}
logoutSuccess();
toast.error(t("deletePop"));
setManageDeleteAccount((prev) => ({ ...prev, IsDeleteAccount: false }));
}
} finally {
setManageDeleteAccount((prev) => ({ ...prev, IsDeleting: false }));
}
};
return (
<>
<header className="py-5 border-b">
<nav className="container">
<div className="space-between">
<CustomLink href="/">
<CustomImage
src={settings?.header_logo}
alt="logo"
width={195}
height={52}
className="w-full h-[52px] object-contain ltr:object-left rtl:object-right max-w-[195px]"
/>
</CustomLink>
{/* desktop category search select */}
{isLargeScreen && (
<div className="flex items-center border leading-none rounded">
<Search />
</div>
)}
<button
className="hidden lg:flex items-center gap-1"
onClick={() => setIsLocationModalOpen(true)}
>
<GrLocation
size={16}
className="flex-shrink-0"
title={locationText ? locationText : t("addLocation")}
/>
<p
className="hidden xl:block text-sm"
title={locationText ? locationText : t("addLocation")}
>
{locationText
? truncate(locationText, 12)
: truncate(t("addLocation"), 12)}
</p>
</button>
<div className="hidden lg:flex items-center gap-2">
{IsLoggedin ? (
<ProfileDropdown
setIsLogout={setIsLogout}
IsLogout={IsLogout}
/>
) : (
<>
<button
onClick={() => setIsLoginOpen(true)}
title={t("login")}
>
{truncate(t("login"), 12)}
</button>
<span className="border-l h-6 self-center"></span>
<button
onClick={() => setIsRegisterModalOpen(true)}
title={t("register")}
>
{truncate(t("register"), 12)}
</button>
</>
)}
<button
className="bg-primary px-2 xl:px-4 py-2 items-center text-white rounded-md flex gap-1"
disabled={IsAdListingClicked}
onClick={handleAdListing}
title={t("adListing")}
>
{IsAdListingClicked ? (
<Loader2 size={18} className="animate-spin" />
) : (
<IoIosAddCircleOutline size={18} />
)}
<span className="hidden xl:inline">
{truncate(t("adListing"), 12)}
</span>
</button>
<LanguageDropdown />
</div>
<HomeMobileMenu
setIsLocationModalOpen={setIsLocationModalOpen}
setIsRegisterModalOpen={setIsRegisterModalOpen}
setIsLogout={setIsLogout}
locationText={locationText}
handleAdListing={handleAdListing}
IsAdListingClicked={IsAdListingClicked}
setManageDeleteAccount={setManageDeleteAccount}
/>
</div>
{!isLargeScreen && (
<div className="flex items-center border leading-none rounded mt-2">
<Search />
</div>
)}
</nav>
</header>
{isCategoryLoading && !cateData.length ? (
<HeaderCategoriesSkeleton />
) : (
cateData &&
cateData.length > 0 && <HeaderCategories cateData={cateData} />
)}
<LoginModal
key={IsLoginOpen}
IsLoginOpen={IsLoginOpen}
setIsRegisterModalOpen={setIsRegisterModalOpen}
/>
<RegisterModal
setIsMailSentSuccess={setIsMailSentSuccess}
IsRegisterModalOpen={IsRegisterModalOpen}
setIsRegisterModalOpen={setIsRegisterModalOpen}
key={`${IsRegisterModalOpen}-register-modal`}
/>
<MailSentSuccessModal
IsMailSentSuccess={IsMailSentSuccess}
setIsMailSentSuccess={setIsMailSentSuccess}
/>
{/* Reusable Alert Dialog for Logout */}
<ReusableAlertDialog
open={IsLogout}
onCancel={() => setIsLogout(false)}
onConfirm={handleLogout}
title={t("confirmLogout")}
description={t("areYouSureToLogout")}
cancelText={t("cancel")}
confirmText={t("yes")}
confirmDisabled={IsLoggingOut}
/>
{/* Reusable Alert Dialog for Updating Profile */}
<ReusableAlertDialog
open={IsUpdatingProfile}
onCancel={() => setIsUpdatingProfile(false)}
onConfirm={handleUpdateProfile}
title={t("updateProfile")}
description={t("youNeedToUpdateProfile")}
confirmText={t("yes")}
/>
{!isLargeScreen && (
<ReusableAlertDialog
open={manageDeleteAccount?.IsDeleteAccount}
onCancel={() =>
setManageDeleteAccount((prev) => ({
...prev,
IsDeleteAccount: false,
}))
}
onConfirm={handleDeleteAcc}
title={t("areYouSure")}
description={
<ul className="list-disc list-inside mt-2">
<li>{t("adsAndTransactionWillBeDeleted")}</li>
<li>{t("accountsDetailsWillNotRecovered")}</li>
<li>{t("subWillBeCancelled")}</li>
<li>{t("savedMesgWillBeLost")}</li>
</ul>
}
cancelText={t("cancel")}
confirmText={t("yes")}
confirmDisabled={manageDeleteAccount?.IsDeleting}
/>
)}
<LocationModal
key={`${IsLocationModalOpen}-location-modal`}
IsLocationModalOpen={IsLocationModalOpen}
setIsLocationModalOpen={setIsLocationModalOpen}
/>
<UnauthorizedModal />
<DeleteAccountVerifyOtpModal
isOpen={IsVerifyOtpBeforeDelete}
setIsOpen={setIsVerifyOtpBeforeDelete}
key={`${IsVerifyOtpBeforeDelete}-delete-account-verify-otp-modal`}
pathname={pathname}
navigate={navigate}
/>
</>
);
};
export default HomeHeader;