block,unblock,report, shortlisted changes in detail page
This commit is contained in:
parent
30bea864e5
commit
11cc3c58b6
@ -76,7 +76,10 @@ export const API_ENDPOINTS = {
|
|||||||
SUBSCRIPTION_PLANS: "subscription-plans",
|
SUBSCRIPTION_PLANS: "subscription-plans",
|
||||||
SUBSCRIPTION_PURCHASE_RAZORPAY: "subscription-purchase-razorpay",
|
SUBSCRIPTION_PURCHASE_RAZORPAY: "subscription-purchase-razorpay",
|
||||||
SUBSCRIPTION_HISTORY: "subscription-history",
|
SUBSCRIPTION_HISTORY: "subscription-history",
|
||||||
|
|
||||||
|
//Report and Block profile
|
||||||
|
REPORT_PROFILE : "report_profile",
|
||||||
|
BLOCK_PROFILE: "block_profile",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://www.thirukalyanam.amrithaa.net/backend/api/report_profile?profile_id=151&reason=test
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default function MatchesInterface() {
|
|||||||
const selectedTab = filterType || "all_matches";
|
const selectedTab = filterType || "all_matches";
|
||||||
const isPaidMember = filters.isPaidMember;
|
const isPaidMember = filters.isPaidMember;
|
||||||
|
|
||||||
const { ref, inView } = useInView({
|
const { ref, inView } = useInView({
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
rootMargin: "300px"
|
rootMargin: "300px"
|
||||||
});
|
});
|
||||||
@ -48,30 +48,30 @@ const { ref, inView } = useInView({
|
|||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
} = useProfiles(filters);
|
} = useProfiles(filters);
|
||||||
const profiles =
|
const profiles =
|
||||||
profilesData?.pages.flatMap((page) => page?.data|| []) || [];
|
profilesData?.pages.flatMap((page) => page?.data || []) || [];
|
||||||
|
|
||||||
// const { ref, inView } = useInView();
|
// const { ref, inView } = useInView();
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (inView && hasNextPage && !isFetchingNextPage) {
|
// if (inView && hasNextPage && !isFetchingNextPage) {
|
||||||
// fetchNextPage();
|
// fetchNextPage();
|
||||||
// }
|
// }
|
||||||
// }, [inView, hasNextPage, isFetchingNextPage]);
|
// }, [inView, hasNextPage, isFetchingNextPage]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inView && hasNextPage && !isFetchingNextPage) {
|
if (inView && hasNextPage && !isFetchingNextPage) {
|
||||||
|
|
||||||
setShowSkeleton(true); // show skeleton
|
setShowSkeleton(true); // show skeleton
|
||||||
|
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
fetchNextPage();
|
fetchNextPage();
|
||||||
setShowSkeleton(false); // hide skeleton after API call
|
setShowSkeleton(false); // hide skeleton after API call
|
||||||
}, 120); // 0.5 seconds
|
}, 120); // 0.5 seconds
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -84,10 +84,10 @@ useEffect(() => {
|
|||||||
console.log("Fetched profiles:", profiles);
|
console.log("Fetched profiles:", profiles);
|
||||||
|
|
||||||
console.log({
|
console.log({
|
||||||
inView,
|
inView,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
isFetchingNextPage,
|
isFetchingNextPage,
|
||||||
});
|
});
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
id: "all_matches",
|
id: "all_matches",
|
||||||
@ -159,169 +159,165 @@ useEffect(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<>
|
<>
|
||||||
|
|
||||||
{/* <div className="grid grid-cols-1 md:grid-cols-[300px_auto] md:px-4 gap-2 md:gap-10" > */}
|
{/* <div className="grid grid-cols-1 md:grid-cols-[300px_auto] md:px-4 gap-2 md:gap-10" > */}
|
||||||
<div className="flex flex-col md:flex-row my-6" >
|
<div className="flex flex-col md:flex-row my-6" >
|
||||||
|
|
||||||
|
|
||||||
{/* Left Sidebar - Fixed on desktop, scrollable on mobile */}
|
{/* Left Sidebar - Fixed on desktop, scrollable on mobile */}
|
||||||
|
|
||||||
<div className="w-full md:w-80">
|
<div className="w-full md:w-80">
|
||||||
<div
|
<div
|
||||||
className="relative rounded-[15px] border border-gray-200 bg-white my-6
|
className="relative rounded-[15px] border border-gray-200 bg-white my-6
|
||||||
shadow-xl h-[450px] md:h-[calc(100vh-200px)] overflow-y-auto md:sticky md:top-[120px] custom-sidebar-scrollbar"
|
shadow-xl h-[450px] md:h-[calc(100vh-200px)] overflow-y-auto md:sticky md:top-[120px] custom-sidebar-scrollbar"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
<div className="py-2 px-4 sticky top-0 bg-[#fff] ">
|
<div className="py-2 px-4 sticky top-0 bg-[#fff] ">
|
||||||
<h2 className="text-xl font-bold text-green-900 mb-4 mt-6 first:mt-0">
|
<h2 className="text-xl font-bold text-green-900 mb-4 mt-6 first:mt-0">
|
||||||
Filter Matches </h2>
|
Filter Matches </h2>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-6 px-4 h-full" >
|
<div className="py-6 px-4 h-full" >
|
||||||
{tabs.map((tab) => {
|
{tabs.map((tab) => {
|
||||||
const showCategory = tab.category !== currentCategory;
|
const showCategory = tab.category !== currentCategory;
|
||||||
if (showCategory) {
|
if (showCategory) {
|
||||||
currentCategory = tab.category;
|
currentCategory = tab.category;
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={tab.id}>
|
|
||||||
{showCategory && (
|
|
||||||
<h2 className="text-xs font-bold text-green-700 uppercase tracking-widest mb-3 mt-6 first:mt-0 px-2 opacity-80">
|
|
||||||
{tab.category}
|
|
||||||
</h2>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
const finalFilterType = tab.id === "all_matches" ? "" : tab.id;
|
|
||||||
dispatch(updateFilter({ filter_type: finalFilterType }));
|
|
||||||
}}
|
|
||||||
|
|
||||||
className={`p-4 rounded-lg mb-3 cursor-pointer transition-all ${
|
|
||||||
selectedTab === tab.id
|
|
||||||
? "bg-green-50 border-l-4 border-green-600"
|
|
||||||
: "hover:bg-gray-50"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div
|
|
||||||
className={`mt-1 ${
|
|
||||||
selectedTab === tab.id
|
|
||||||
? "text-green-600"
|
|
||||||
: "text-gray-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab.icon}
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3
|
|
||||||
className={`font-semibold text-base ${
|
|
||||||
selectedTab === tab.id
|
|
||||||
? "text-green-900"
|
|
||||||
: "text-gray-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{tab.title}
|
|
||||||
</h3>
|
|
||||||
<svg
|
|
||||||
className={`w-5 h-5 ${
|
|
||||||
selectedTab === tab.id
|
|
||||||
? "text-green-600"
|
|
||||||
: "text-gray-400"
|
|
||||||
}`}
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M9 5l7 7-7 7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-600 mt-1">
|
|
||||||
{tab.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Right Content Area - Scrollable */}
|
|
||||||
<div className=" px-2 py-8 w-full md:w-9/12 ">
|
|
||||||
<div className="w-[100%] max-w-[1200px] mx-auto">
|
|
||||||
<div className="flex justify-between gap-2 itemes-center mb-8">
|
|
||||||
<h1 className="text-[24px] font-bold text-gray-900 ">
|
|
||||||
{tabs.find((t) => t.id === selectedTab)?.title}
|
|
||||||
</h1>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<div className="relative cursor-pointer" onClick={() => {
|
|
||||||
if (isPaidMember) {
|
|
||||||
navigate("/horoscoper-generate");
|
|
||||||
} else {
|
|
||||||
toast.error("Star Matching is locked for free members");
|
|
||||||
}
|
}
|
||||||
}}>
|
|
||||||
<img
|
return (
|
||||||
src={horoscope}
|
<div key={tab.id}>
|
||||||
className={!isPaidMember ? "opacity-50 blur-[1px]" : ""}
|
{showCategory && (
|
||||||
/>
|
<h2 className="text-xs font-bold text-green-700 uppercase tracking-widest mb-3 mt-6 first:mt-0 px-2 opacity-80">
|
||||||
{!isPaidMember && (
|
{tab.category}
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
</h2>
|
||||||
<Lock className="w-4 h-4 text-black" />
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
const finalFilterType = tab.id === "all_matches" ? "" : tab.id;
|
||||||
|
dispatch(updateFilter({ filter_type: finalFilterType }));
|
||||||
|
}}
|
||||||
|
|
||||||
|
className={`p-4 rounded-lg mb-3 cursor-pointer transition-all ${selectedTab === tab.id
|
||||||
|
? "bg-green-50 border-l-4 border-green-600"
|
||||||
|
: "hover:bg-gray-50"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div
|
||||||
|
className={`mt-1 ${selectedTab === tab.id
|
||||||
|
? "text-green-600"
|
||||||
|
: "text-gray-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab.icon}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h3
|
||||||
|
className={`font-semibold text-base ${selectedTab === tab.id
|
||||||
|
? "text-green-900"
|
||||||
|
: "text-gray-900"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{tab.title}
|
||||||
|
</h3>
|
||||||
|
<svg
|
||||||
|
className={`w-5 h-5 ${selectedTab === tab.id
|
||||||
|
? "text-green-600"
|
||||||
|
: "text-gray-400"
|
||||||
|
}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M9 5l7 7-7 7"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-600 mt-1">
|
||||||
|
{tab.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
);
|
||||||
</div>
|
})}
|
||||||
<FilterModal />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-2">
|
</div>
|
||||||
{isLoading && !isFetchingNextPage ? (
|
{/* Right Content Area - Scrollable */}
|
||||||
[...Array(6)].map((_, i) => <ProfileCardSkeleton key={i} />)
|
<div className=" px-2 py-8 w-full md:w-9/12 ">
|
||||||
) : profiles.length > 0 ? (
|
<div className="w-[100%] max-w-[1200px] mx-auto">
|
||||||
profiles.map((profile) => (
|
<div className="flex justify-between gap-2 itemes-center mb-8">
|
||||||
<ProfileCardUI key={profile.id} profile={profile} />
|
<h1 className="text-[24px] font-bold text-gray-900 ">
|
||||||
))
|
{tabs.find((t) => t.id === selectedTab)?.title}
|
||||||
) : !isLoading && !isFetchingNextPage ? (
|
</h1>
|
||||||
<div className="col-span-full text-center py-10 text-gray-500">
|
<div className="flex gap-2 items-center">
|
||||||
No profiles found
|
<div className="relative cursor-pointer" onClick={() => {
|
||||||
|
if (isPaidMember) {
|
||||||
|
navigate("/horoscoper-generate");
|
||||||
|
} else {
|
||||||
|
toast.error("Star Matching is locked for free members");
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<img
|
||||||
|
src={horoscope}
|
||||||
|
className={!isPaidMember ? "opacity-50 blur-[1px]" : ""}
|
||||||
|
/>
|
||||||
|
{!isPaidMember && (
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<Lock className="w-4 h-4 text-black" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<FilterModal />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-2">
|
||||||
|
{isLoading && !isFetchingNextPage ? (
|
||||||
|
[...Array(6)].map((_, i) => <ProfileCardSkeleton key={i} />)
|
||||||
|
) : profiles.length > 0 ? (
|
||||||
|
profiles.map((profile) => (
|
||||||
|
<ProfileCardUI key={profile.id} profile={profile} />
|
||||||
|
))
|
||||||
|
) : !isLoading && !isFetchingNextPage ? (
|
||||||
|
<div className="col-span-full text-center py-10 text-gray-500">
|
||||||
|
No profiles found
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
|
||||||
{/* {isFetchingNextPage &&
|
{/* {isFetchingNextPage &&
|
||||||
[...Array(5)].map((_, i) => (
|
[...Array(5)].map((_, i) => (
|
||||||
<ProfileCardSkeleton key={`skel-${i}`} />
|
<ProfileCardSkeleton key={`skel-${i}`} />
|
||||||
))} */}
|
))} */}
|
||||||
|
|
||||||
|
|
||||||
{(isFetchingNextPage || showSkeleton) &&
|
{(isFetchingNextPage || showSkeleton) &&
|
||||||
[...Array(6)].map((_, i) => (
|
[...Array(6)].map((_, i) => (
|
||||||
<ProfileCardSkeleton key={`skel-${i}`} />
|
<ProfileCardSkeleton key={`skel-${i}`} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div ref={ref} className="h-[20px]">
|
<div ref={ref} className="h-[20px]">
|
||||||
{!isLoading && !hasNextPage && profiles.length > 0 && (
|
{!isLoading && !hasNextPage && profiles.length > 0 && (
|
||||||
<p className="text-center text-gray-500 py-8">
|
<p className="text-center text-gray-500 py-8">
|
||||||
You've reached the end.
|
You've reached the end.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -336,7 +332,7 @@ useEffect(() => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>{`
|
<style>{`
|
||||||
.custom-sidebar-scrollbar::-webkit-scrollbar {
|
.custom-sidebar-scrollbar::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -180,11 +180,13 @@ const DailyRecommendedCard = ({ profiles: initialProfiles = [] }) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
try {
|
try {
|
||||||
const res = await shortlistProfile(profile.id);
|
const res = await shortlistProfile(profile.id);
|
||||||
if (res.status === "success") {
|
console.log(res);
|
||||||
|
if (res.status === "success" || res.message) {
|
||||||
setIsShortlisted(!isShortlisted);
|
setIsShortlisted(!isShortlisted);
|
||||||
toast.success(res.data.message || "Updated shortlist status");
|
toast.success(res.message || "Updated shortlist status");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
toast.error("Failed to update shortlist");
|
toast.error("Failed to update shortlist");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -28,7 +28,9 @@ import "swiper/css/pagination";
|
|||||||
import "swiper/css/thumbs";
|
import "swiper/css/thumbs";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { sendInterest, shortlistProfile } from "../../services/shortlistapi";
|
import { sendInterest, shortlistProfile } from "../../services/shortlistapi";
|
||||||
|
import { reportProfile, blockProfile } from "../../services/profileActionApi";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
|
import ReportModel from "../../feature/ReportModel";
|
||||||
import UpgradeModal from "../common/UpgradeModal";
|
import UpgradeModal from "../common/UpgradeModal";
|
||||||
import axiosInstance, { apiForFiles } from "../../api/axiosInstance";
|
import axiosInstance, { apiForFiles } from "../../api/axiosInstance";
|
||||||
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
||||||
@ -69,6 +71,8 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
const [isCreatingChat, setIsCreatingChat] = useState(false);
|
const [isCreatingChat, setIsCreatingChat] = useState(false);
|
||||||
const [modalTitle, setModalTitle] = useState("");
|
const [modalTitle, setModalTitle] = useState("");
|
||||||
const [modalMessage, setModalMessage] = useState("");
|
const [modalMessage, setModalMessage] = useState("");
|
||||||
|
const [reportModalOpen, setReportModalOpen] = useState(false);
|
||||||
|
const [reportActionType, setReportActionType] = useState("report");
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
@ -308,10 +312,45 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
const handleShortlist = async () => {
|
const handleShortlist = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await shortlistProfile(profile.id);
|
const res = await shortlistProfile(profile.id);
|
||||||
toast.success(res.message || "Profile shortlisted successfully");
|
toast.success(res.message || "Updated shortlist status");
|
||||||
if (onRefresh) onRefresh(false);
|
if (onRefresh) onRefresh(false);
|
||||||
|
queryClient.invalidateQueries();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.message || "Failed to shortlist");
|
toast.error(error.message || "Failed to update shortlist");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const openReportModal = (type) => {
|
||||||
|
setReportActionType(type);
|
||||||
|
setReportModalOpen(true);
|
||||||
|
setShowMenu(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReportAction = async (reason) => {
|
||||||
|
try {
|
||||||
|
if (reportActionType === 'report') {
|
||||||
|
const res = await reportProfile(profile.id, reason);
|
||||||
|
toast.success(res?.message || "Profile Reported successfully");
|
||||||
|
} else {
|
||||||
|
const res = await blockProfile(profile.id, reason);
|
||||||
|
toast.success(res?.message || "Profile Blocked successfully");
|
||||||
|
}
|
||||||
|
if (onRefresh) onRefresh(false);
|
||||||
|
queryClient.invalidateQueries();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error?.message || `Failed to ${reportActionType === 'report' ? 'Report' : 'Block'}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUnblock = async () => {
|
||||||
|
try {
|
||||||
|
// Calling the same blockProfile API without a model/reason to trigger unblocking
|
||||||
|
const res = await blockProfile(profile.id, "");
|
||||||
|
toast.success(res?.message || "Profile Unblocked successfully");
|
||||||
|
if (onRefresh) onRefresh(false);
|
||||||
|
queryClient.invalidateQueries();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error?.message || "Failed to Unblock");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -339,6 +378,12 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-50 min-h-screen py-8 px-4 sm:px-6 lg:px-8">
|
<div className="bg-gray-50 min-h-screen py-8 px-4 sm:px-6 lg:px-8">
|
||||||
|
<ReportModel
|
||||||
|
open={reportModalOpen}
|
||||||
|
onClose={() => setReportModalOpen(false)}
|
||||||
|
onSubmit={handleReportAction}
|
||||||
|
actionType={reportActionType}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className="max-w-[1200px] mx-auto
|
className="max-w-[1200px] mx-auto
|
||||||
bg-white rounded-3xl shadow-xl shadow-gray-200/50 border border-gray-100 overflow-hidden"
|
bg-white rounded-3xl shadow-xl shadow-gray-200/50 border border-gray-100 overflow-hidden"
|
||||||
@ -424,9 +469,10 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-xl border z-10">
|
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-xl border z-10">
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowMenu(false); handleShortlist(); }}
|
onClick={() => { setShowMenu(false); handleShortlist(); }}
|
||||||
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3"
|
className={`w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 ${profile.is_shortlisted === 1 ? 'text-green-700' : ''}`}
|
||||||
>
|
>
|
||||||
<Bookmark className="w-4 h-4" /> Shortlist
|
<Bookmark className={`w-4 h-4 ${profile.is_shortlisted === 1 ? 'fill-current text-green-700' : ''}`} />
|
||||||
|
{profile.is_shortlisted === 1 ? "Shortlisted" : "Shortlist"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowMenu(false); navigate("/chat"); }}
|
onClick={() => { setShowMenu(false); navigate("/chat"); }}
|
||||||
@ -434,10 +480,26 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
>
|
>
|
||||||
<MessageCircle className="w-4 h-4" /> Send Message
|
<MessageCircle className="w-4 h-4" /> Send Message
|
||||||
</button>
|
</button>
|
||||||
<button className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3">
|
<button
|
||||||
<Ban className="w-4 h-4" /> Block Profile
|
onClick={() => {
|
||||||
|
setShowMenu(false);
|
||||||
|
if (profile.is_blocked) {
|
||||||
|
handleUnblock();
|
||||||
|
} else {
|
||||||
|
openReportModal('block');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<Ban className="w-4 h-4" /> {profile.is_blocked ? "Unblock Profile" : "Block Profile"}
|
||||||
</button>
|
</button>
|
||||||
<button className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 text-red-600">
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowMenu(false);
|
||||||
|
openReportModal('report');
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 text-red-600"
|
||||||
|
>
|
||||||
<Flag className="w-4 h-4" /> Report Profile
|
<Flag className="w-4 h-4" /> Report Profile
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
72
src/feature/ReportModel.jsx
Normal file
72
src/feature/ReportModel.jsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Box, Button, TextField } from "@mui/material";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
const ReportModel = ({ open, onClose, onSubmit, actionType }) => {
|
||||||
|
const [reason, setReason] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setReason("");
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
onSubmit(reason);
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ background: "#f5fbff" }}>
|
||||||
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||||
|
<span className="capitalize" style={{ textTransform: 'capitalize' }}>
|
||||||
|
{actionType === 'block' ? 'Block Profile' : 'Report Profile'}
|
||||||
|
</span>
|
||||||
|
<IconButton onClick={onClose} aria-label="close">
|
||||||
|
<X size={20} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent dividers>
|
||||||
|
<Box mt={1}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
variant="outlined"
|
||||||
|
placeholder={`Enter your reason for ${actionType === 'block' ? 'blocking' : 'reporting'} this profile...`}
|
||||||
|
value={reason}
|
||||||
|
onChange={(e) => setReason(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ p: 2, background: "#f5fbff" }}>
|
||||||
|
<Button onClick={onClose} color="inherit">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
variant="contained"
|
||||||
|
disabled={!reason.trim()}
|
||||||
|
sx={{
|
||||||
|
bgcolor: actionType === 'block' ? '#d32f2f' : '#034E08',
|
||||||
|
'&:hover': {
|
||||||
|
bgcolor: actionType === 'block' ? '#b71c1c' : '#023806',
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{actionType === 'block' ? 'Block' : 'Submit Report'}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportModel;
|
||||||
@ -23,7 +23,7 @@ export const getReportedProfiles = async () => {
|
|||||||
|
|
||||||
export const unblockProfile = async (profileId) => {
|
export const unblockProfile = async (profileId) => {
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.post(`unblock_profile?profile_id=${profileId}`);
|
const response = await axiosInstance.post(`${API_ENDPOINTS.BLOCK_PROFILE}?profile_id=${profileId}`);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error unblocking profile:", error);
|
console.error("Error unblocking profile:", error);
|
||||||
@ -61,3 +61,18 @@ export const updateInterestStatus = async (id, status) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const reportProfile = async (profileId, reason) => {
|
||||||
|
const response = await axiosInstance.post(`${API_ENDPOINTS.REPORT_PROFILE}?profile_id=${profileId}&reason=${reason}`);
|
||||||
|
if (response.data?.status === "error") {
|
||||||
|
throw new Error(response.data.message || "Failed to report");
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const blockProfile = async (profileId, reason) => {
|
||||||
|
const response = await axiosInstance.post(`${API_ENDPOINTS.BLOCK_PROFILE}?profile_id=${profileId}&reason=${reason}`);
|
||||||
|
if (response.data?.status === "error") {
|
||||||
|
throw new Error(response.data.message || "Failed to block");
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user