Refine profile interaction logic, synchronize UI components, and update API request formatting to use FormData
This commit is contained in:
parent
b3d33aca9c
commit
30bea864e5
@ -26,6 +26,7 @@ import { getHeaderDetails } from "../../api/preview.api";
|
|||||||
export default function ProfileCardUI({ profile }) {
|
export default function ProfileCardUI({ profile }) {
|
||||||
const [isLiked, setIsLiked] = useState(false);
|
const [isLiked, setIsLiked] = useState(false);
|
||||||
const [isShortlisted, setIsShortlisted] = useState(profile.is_shortlisted === 1);
|
const [isShortlisted, setIsShortlisted] = useState(profile.is_shortlisted === 1);
|
||||||
|
const [isLimitModalOpen, setIsLimitModalOpen] = useState(false);
|
||||||
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
||||||
const [isViewContactModalOpen, setIsViewContactModalOpen] = useState(false);
|
const [isViewContactModalOpen, setIsViewContactModalOpen] = useState(false);
|
||||||
const [isInterestStatusModalOpen, setIsInterestStatusModalOpen] = useState(false);
|
const [isInterestStatusModalOpen, setIsInterestStatusModalOpen] = useState(false);
|
||||||
@ -60,14 +61,19 @@ export default function ProfileCardUI({ profile }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await axiosInstance.post(`${API_ENDPOINTS.INTEREST_SEND}?profile_id=${profile.id}`);
|
await sendInterest(profile.id);
|
||||||
setIsLiked(true);
|
setIsLiked(true);
|
||||||
toast.success(`Interest sent to ${profile.name}!`, {
|
toast.success(`Interest sent to ${profile.name}!`, {
|
||||||
icon: '❤️',
|
icon: '❤️',
|
||||||
style: { borderRadius: '10px', background: '#333', color: '#fff' }
|
style: { borderRadius: '10px', background: '#333', color: '#fff' }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to send interest.");
|
const msg = error.message || "";
|
||||||
|
if (msg.toLowerCase().includes("limit") || msg.toLowerCase().includes("reached")) {
|
||||||
|
setIsLimitModalOpen(true);
|
||||||
|
} else {
|
||||||
|
toast.error(msg || "Failed to send interest");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -227,8 +233,8 @@ export default function ProfileCardUI({ profile }) {
|
|||||||
const handleShortlistClick = async (e) => {
|
const handleShortlistClick = async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
try {
|
try {
|
||||||
const res = await axiosInstance.post(`${API_ENDPOINTS.SHORTLIST_API}?profile_id=${profile.id}`);
|
const res = await shortlistProfile(profile.id);
|
||||||
if (res.data?.status === "success") {
|
if (res.status === "success") {
|
||||||
setIsShortlisted(!isShortlisted);
|
setIsShortlisted(!isShortlisted);
|
||||||
toast.success(res.data.message || "Updated shortlist status");
|
toast.success(res.data.message || "Updated shortlist status");
|
||||||
}
|
}
|
||||||
@ -401,10 +407,12 @@ export default function ProfileCardUI({ profile }) {
|
|||||||
|
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
|
<LimitReachedModal
|
||||||
|
isOpen={isLimitModalOpen}
|
||||||
|
onClose={() => setIsLimitModalOpen(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
||||||
onClick={() => navigate(`/profile-details/${profile.id}`)}
|
onClick={() => navigate(`/profile-details/${profile.id}`)}
|
||||||
className="w-full max-w-sm rounded-[10px] shadow-xl overflow-hidden border border-green-200 bg-white cursor-pointer hover:shadow-2xl transition-all duration-300"
|
className="w-full max-w-sm rounded-[10px] shadow-xl overflow-hidden border border-green-200 bg-white cursor-pointer hover:shadow-2xl transition-all duration-300"
|
||||||
>
|
>
|
||||||
@ -572,13 +580,41 @@ export default function ProfileCardUI({ profile }) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LimitReachedModal = ({ isOpen, onClose }) => {
|
||||||
|
if (!isOpen) return null;
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-[10000] p-4">
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0.9, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
className="bg-white rounded-3xl p-8 max-w-sm w-full text-center shadow-2xl relative"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute top-4 right-4 text-gray-400 hover:text-gray-600"
|
||||||
|
>
|
||||||
|
<X className="w-6 h-6" />
|
||||||
|
</button>
|
||||||
|
<div className="w-20 h-20 bg-amber-50 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||||
|
<Clock className="w-10 h-10 text-amber-600" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-bold text-gray-900 mb-3">Limit Reached!</h3>
|
||||||
|
<p className="text-gray-600 mb-8 leading-relaxed">
|
||||||
|
You've reached your daily limit for sending interests. Please try again tomorrow or upgrade your plan for more daily connections!
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="w-full py-4 bg-[#034E08] text-white rounded-2xl font-bold hover:bg-green-800 transition-colors"
|
||||||
|
>
|
||||||
|
Got it
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -439,14 +439,13 @@ const ProfileHeader = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppBar position="sticky" sx={{ backgroundColor: "#fff" }}>
|
<AppBar position="sticky" sx={{ backgroundColor: "#fff", zIndex: 1000 }}>
|
||||||
<Container maxWidth="xl">
|
<Container maxWidth="xl">
|
||||||
<Toolbar disableGutters>
|
<Toolbar disableGutters>
|
||||||
<Box onClick={() => navigate("/")} sx={{ display: { xs: "none", md: "flex" }, mr: 1 }}>
|
<Box onClick={() => navigate("/")} sx={{ display: { xs: "none", md: "flex" }, mr: 1 }}>
|
||||||
<LazyImage
|
<LazyImage
|
||||||
src={Logo}
|
src={Logo}
|
||||||
className="w-full h-[70px] my-2 rounded-lg object-cover cursor-pointer"
|
className="h-[50px] w-auto my-1 rounded-lg object-contain cursor-pointer"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -462,8 +461,7 @@ const ProfileHeader = () => {
|
|||||||
>
|
>
|
||||||
<LazyImage
|
<LazyImage
|
||||||
src={Logo}
|
src={Logo}
|
||||||
className="w-full h-[50px] rounded-lg object-cover"
|
className="h-[40px] w-auto rounded-lg object-contain"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
import axiosInstance from "../../api/axiosInstance";
|
import axiosInstance from "../../api/axiosInstance";
|
||||||
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
||||||
|
import { sendInterest, shortlistProfile } from '../../services/shortlistapi';
|
||||||
import UpgradeModal from '../common/UpgradeModal';
|
import UpgradeModal from '../common/UpgradeModal';
|
||||||
|
|
||||||
// Custom Icons
|
// Custom Icons
|
||||||
@ -137,14 +138,19 @@ const DailyRecommendedCard = ({ profiles: initialProfiles = [] }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await axiosInstance.post(`${API_ENDPOINTS.INTEREST_SEND}?profile_id=${profileId}`);
|
await sendInterest(profileId);
|
||||||
toast.success(`Interest sent to ${name}!`, {
|
toast.success(`Interest sent to ${name}!`, {
|
||||||
icon: '❤️',
|
icon: '❤️',
|
||||||
style: { borderRadius: '10px', background: '#333', color: '#fff' }
|
style: { borderRadius: '10px', background: '#333', color: '#fff' }
|
||||||
});
|
});
|
||||||
setActiveProfiles(prev => prev.filter(p => p.id !== profileId));
|
setActiveProfiles(prev => prev.filter(p => p.id !== profileId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to send interest. Please try again.");
|
const msg = error.message || "";
|
||||||
|
if (msg.toLowerCase().includes("limit") || msg.toLowerCase().includes("reached")) {
|
||||||
|
setIsUpgradeModalOpen(true);
|
||||||
|
} else {
|
||||||
|
toast.error(msg || "Failed to send interest. Please try again.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -173,8 +179,8 @@ const DailyRecommendedCard = ({ profiles: initialProfiles = [] }) => {
|
|||||||
const handleShortlistClick = async (e) => {
|
const handleShortlistClick = async (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
try {
|
try {
|
||||||
const res = await axiosInstance.post(`${API_ENDPOINTS.SHORTLIST_API}?profile_id=${profile.id}`);
|
const res = await shortlistProfile(profile.id);
|
||||||
if (res.data?.status === "success") {
|
if (res.status === "success") {
|
||||||
setIsShortlisted(!isShortlisted);
|
setIsShortlisted(!isShortlisted);
|
||||||
toast.success(res.data.message || "Updated shortlist status");
|
toast.success(res.data.message || "Updated shortlist status");
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -81,26 +81,14 @@ const PartnerPreferences = ({ data }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='grid grid-cols-1 gap-2 md:grid-cols-2 mb-8 pt-4'>
|
<div className='max-w-3xl mx-auto mb-8 pt-4'>
|
||||||
{/* Basic Preferences Section */}
|
{/* Partner Preferences Section */}
|
||||||
<div className="bg-white rounded-2xl shadow-lg overflow-hidden">
|
<div className="bg-white rounded-2xl shadow-lg overflow-hidden border border-gray-100">
|
||||||
<div className="flex items-center justify-between mb-4 bg-[#f5fbff] pt-4 pb-4 px-6">
|
<div className="flex items-center justify-between bg-[#f5fbff] py-4 px-6 border-b border-gray-100">
|
||||||
<h2 className="text-lg font-bold text-gray-800">Basic Preferences</h2>
|
<h2 className="text-lg font-bold text-gray-800">Partner Preference Details</h2>
|
||||||
</div>
|
|
||||||
<div className="space-y-1 p-6">
|
|
||||||
{basicPreferences.map((pref, index) => (
|
|
||||||
<PreferenceItem key={index} {...pref} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Other Preferences Section */}
|
|
||||||
<div className="bg-white rounded-2xl shadow-lg overflow-hidden">
|
|
||||||
<div className="flex items-center justify-between mb-4 bg-[#f5fbff] pt-4 pb-4 px-6">
|
|
||||||
<h2 className="text-lg font-bold text-gray-800">Professional & Location</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1 p-6">
|
<div className="space-y-1 p-6">
|
||||||
{religiousPreferences.map((pref, index) => (
|
{[...basicPreferences, ...religiousPreferences].map((pref, index) => (
|
||||||
<PreferenceItem key={index} {...pref} />
|
<PreferenceItem key={index} {...pref} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,18 +11,19 @@ const ProfileDetailPage = () => {
|
|||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const fetchDetail = async (showLoading = true) => {
|
||||||
|
if (showLoading) setLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await getProfileDetail(id);
|
||||||
|
setData(res);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch profile details:", error);
|
||||||
|
} finally {
|
||||||
|
if (showLoading) setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchDetail = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const res = await getProfileDetail(id);
|
|
||||||
setData(res);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to fetch profile details:", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (id) {
|
if (id) {
|
||||||
fetchDetail();
|
fetchDetail();
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ const ProfileDetailPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="w-[100%] max-w-[1400px] mx-auto my-10">
|
<div className="w-[100%] max-w-[1400px] mx-auto my-10">
|
||||||
<MatrimonyProfile data={data} />
|
<MatrimonyProfile data={data} onRefresh={fetchDetail} />
|
||||||
<PartnerPreferences data={data} />
|
<PartnerPreferences data={data} />
|
||||||
<MatchingList matches={data.all_matches} />
|
<MatchingList matches={data.all_matches} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,22 +1,38 @@
|
|||||||
import axiosInstance from "../api/axiosInstance";
|
import axiosInstance, { apiForFiles } from "../api/axiosInstance";
|
||||||
import { API_ENDPOINTS } from "../api/apiEndpoints";
|
import { API_ENDPOINTS } from "../api/apiEndpoints";
|
||||||
|
|
||||||
export const shortlistProfile = async (profileId) => {
|
export const shortlistProfile = async (profileId) => {
|
||||||
const response = await axiosInstance.post(`${API_ENDPOINTS.SHORTLIST_API}?profile_id=${profileId}`);
|
try {
|
||||||
if (response.data?.status === "error") {
|
const formData = new FormData();
|
||||||
throw new Error(response.data.message || "Failed to shortlist");
|
formData.append("profile_id", profileId);
|
||||||
|
const response = await apiForFiles.post(API_ENDPOINTS.SHORTLIST_API, formData);
|
||||||
|
if (response.data?.status === "error") {
|
||||||
|
throw new Error(response.data.message || "Failed to shortlist");
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
const message = error.response?.data?.message || error.message || "Failed to shortlist";
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
return response.data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendInterest = async (profileId) => {
|
export const sendInterest = async (profileId) => {
|
||||||
const response = await axiosInstance.post(`interest_send`, {
|
try {
|
||||||
profile_id: profileId // ✅ sent in request body
|
const formData = new FormData();
|
||||||
});
|
formData.append("profile_id", profileId);
|
||||||
if (response.data?.status === "error") {
|
|
||||||
throw new Error(response.data.message || "Failed to send interest");
|
// Using apiForFiles which is pre-configured for multipart/form-data
|
||||||
|
// This is more robust for PHP backends expecting form data
|
||||||
|
const response = await apiForFiles.post(API_ENDPOINTS.INTEREST_SEND, formData);
|
||||||
|
|
||||||
|
if (response.data?.status === "error") {
|
||||||
|
throw new Error(response.data.message || "Failed to send interest");
|
||||||
|
}
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
const message = error.response?.data?.message || error.message || "Failed to send interest";
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
return response.data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const declineProfile = async (profileId) => {
|
export const declineProfile = async (profileId) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user