Call history, Clear chat
This commit is contained in:
parent
10477e0932
commit
5e1afc5ccc
254
src/components/chat/CallHistory.jsx
Normal file
254
src/components/chat/CallHistory.jsx
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { ArrowLeft, MoreVertical, Loader2 } from 'lucide-react';
|
||||||
|
import { getCallHistoryMyList, getCallHistoryOthersList, deleteCallHistory } from '../../services/chatApi';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { getHeaderDetails } from '../../api/preview.api';
|
||||||
|
|
||||||
|
const CallHistory = ({ onBack }) => {
|
||||||
|
const [activeTab, setActiveTab] = useState('me');
|
||||||
|
const [historyList, setHistoryList] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const { data: headerData } = useQuery({
|
||||||
|
queryKey: ["headerDetails"],
|
||||||
|
queryFn: getHeaderDetails,
|
||||||
|
});
|
||||||
|
// console.log("headerData", headerData);
|
||||||
|
|
||||||
|
const myDetails = headerData?.myDetails || {};
|
||||||
|
|
||||||
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
|
const [selectedIds, setSelectedIds] = useState([]);
|
||||||
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
setHistoryList([]);
|
||||||
|
try {
|
||||||
|
let res;
|
||||||
|
if (activeTab === 'me') {
|
||||||
|
res = await getCallHistoryMyList();
|
||||||
|
} else {
|
||||||
|
res = await getCallHistoryOthersList();
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = res?.call_list || res?.data || res || [];
|
||||||
|
setHistoryList(Array.isArray(data) ? data : []);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Failed to load call history.");
|
||||||
|
setHistoryList([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
setIsEditMode(false);
|
||||||
|
setSelectedIds([]);
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
|
const toggleSelection = (id) => {
|
||||||
|
if (selectedIds.includes(id)) {
|
||||||
|
setSelectedIds(selectedIds.filter(item => item !== id));
|
||||||
|
} else {
|
||||||
|
setSelectedIds([...selectedIds, id]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
if (selectedIds.length === 0) return;
|
||||||
|
|
||||||
|
setIsDeleting(true);
|
||||||
|
try {
|
||||||
|
const res = await deleteCallHistory(selectedIds);
|
||||||
|
toast.success(res?.message || "History deleted successfully.");
|
||||||
|
setIsEditMode(false);
|
||||||
|
setSelectedIds([]);
|
||||||
|
fetchData();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Failed to delete history.");
|
||||||
|
} finally {
|
||||||
|
setIsDeleting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSkeletons = () => {
|
||||||
|
return Array(5).fill(0).map((_, idx) => (
|
||||||
|
<div key={`skel-${idx}`} className="flex items-center gap-3 p-4 border-b border-gray-100 animate-pulse">
|
||||||
|
<div className="w-12 h-12 bg-gray-200 rounded-full"></div>
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<div className="h-4 bg-gray-200 rounded w-1/3"></div>
|
||||||
|
<div className="h-3 bg-gray-200 rounded w-1/2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-full bg-white relative">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between p-4 border-b border-gray-200 bg-white min-h-[64px]">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
onClick={onBack}
|
||||||
|
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="w-6 h-6 text-gray-800" />
|
||||||
|
</button>
|
||||||
|
<h2 className="text-xl font-bold text-[#1a237e]">Call History</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowMenu(!showMenu)}
|
||||||
|
className="p-2 rounded-full border border-gray-200 hover:bg-gray-50 flex items-center justify-center transition-colors shadow-sm"
|
||||||
|
>
|
||||||
|
<MoreVertical className="w-5 h-5 text-gray-700" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showMenu && (
|
||||||
|
<div className="absolute right-0 mt-2 w-48 bg-white rounded-xl shadow-xl border border-gray-100 z-50 overflow-hidden">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsEditMode(!isEditMode);
|
||||||
|
setShowMenu(false);
|
||||||
|
if (isEditMode) setSelectedIds([]);
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 text-left hover:bg-gray-50 text-gray-700 font-medium text-sm transition-colors"
|
||||||
|
>
|
||||||
|
{isEditMode ? "Cancel Deletion" : "Delete History"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* User Info Banner */}
|
||||||
|
<div className="p-4 flex items-center gap-4 bg-white border-b border-gray-100">
|
||||||
|
<div className="w-14 h-14 rounded-full overflow-hidden border border-gray-200 shadow-sm relative">
|
||||||
|
<img
|
||||||
|
src={myDetails.profile || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"}
|
||||||
|
alt="Profile"
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<h3 className="font-bold text-[#df1d46] text-[17px]">{myDetails.name || "User"}</h3>
|
||||||
|
{myDetails.is_paid && (
|
||||||
|
<span className="bg-[#0c8626] text-white text-[10px] font-bold px-2 py-0.5 rounded uppercase tracking-wider">Free</span> // Or Paid dynamically
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm font-semibold text-gray-800">
|
||||||
|
ID : <span className="font-bold">{myDetails.member_id || myDetails.profile_id || "N/A"}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<div className="flex p-4 gap-4 bg-white sticky top-0 z-10 border-b border-gray-100 shadow-sm">
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab('me')}
|
||||||
|
className={`flex-1 py-3 text-sm font-bold rounded-lg border transition-all ${activeTab === 'me'
|
||||||
|
? 'bg-[#df1d46] text-white border-[#df1d46]'
|
||||||
|
: 'bg-white text-gray-600 border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Viewed By Me
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab('others')}
|
||||||
|
className={`flex-1 py-3 text-sm font-bold rounded-lg border transition-all ${activeTab === 'others'
|
||||||
|
? 'bg-[#df1d46] text-white border-[#df1d46]'
|
||||||
|
: 'bg-white text-gray-600 border-gray-200 hover:bg-gray-50'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Viewed By Others
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content List */}
|
||||||
|
<div className="flex-1 overflow-y-auto pb-20 relative bg-[#fcfcfc]">
|
||||||
|
{loading ? (
|
||||||
|
getSkeletons()
|
||||||
|
) : historyList.length === 0 ? (
|
||||||
|
<div className="flex flex-col items-center justify-center p-8 mt-10">
|
||||||
|
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
|
||||||
|
<span className="text-gray-400">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" /><line x1="22" y1="2" x2="2" y2="22" /></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-500 font-medium">No Call History found</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="divide-y divide-gray-100 border-t border-gray-100">
|
||||||
|
{historyList.map((item, idx) => (
|
||||||
|
<div
|
||||||
|
key={item.id || idx}
|
||||||
|
onClick={() => isEditMode && toggleSelection(item.id)}
|
||||||
|
className={`flex items-center gap-4 p-4 transition-colors cursor-pointer hover:bg-gray-50 ${isEditMode && selectedIds.includes(item.id) ? 'bg-red-50/50' : 'bg-white'}`}
|
||||||
|
>
|
||||||
|
{/* Checkbox for Edit Mode */}
|
||||||
|
{isEditMode && (
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<div className={`w-5 h-5 rounded border flex items-center justify-center transition-colors ${selectedIds.includes(item.id)
|
||||||
|
? 'bg-[#df1d46] border-[#df1d46]'
|
||||||
|
: 'border-gray-300 bg-white'
|
||||||
|
}`}>
|
||||||
|
{selectedIds.includes(item.id) && (
|
||||||
|
<svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7" /></svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<img
|
||||||
|
src={item.profile || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"}
|
||||||
|
alt={item.name}
|
||||||
|
className="w-14 h-14 rounded-full object-cover border border-gray-100 shadow-sm flex-shrink-0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Details */}
|
||||||
|
<div className="flex-1 min-w-0 flex flex-col justify-center">
|
||||||
|
<div className="flex justify-between items-start mb-1.5">
|
||||||
|
<h4 className="font-semibold text-gray-900 text-[16px] truncate pr-2">{item.name}</h4>
|
||||||
|
<span className="text-xs text-gray-500 whitespace-nowrap mt-1">{item.date || item.created_at || "N/A"}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{item.mobile ? (
|
||||||
|
<p className="text-sm font-semibold text-gray-800 truncate">
|
||||||
|
Mobile No : <span className="font-bold">{item.mobile}</span>
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm font-semibold text-gray-800 truncate">
|
||||||
|
ID : <span className="font-bold uppercase">{item.member_id || "N/A"}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Delete Button (Sticky Bottom) */}
|
||||||
|
{isEditMode && selectedIds.length > 0 && (
|
||||||
|
<div className="absolute bottom-4 left-4 right-4 z-20">
|
||||||
|
<button
|
||||||
|
onClick={handleDelete}
|
||||||
|
disabled={isDeleting}
|
||||||
|
className="w-full bg-[#A80000] hover:bg-red-900 text-white font-bold py-4 rounded-xl shadow-lg shadow-red-200 transition-all flex items-center justify-center gap-2 text-lg active:scale-[0.98] disabled:opacity-70 disabled:active:scale-100"
|
||||||
|
>
|
||||||
|
{isDeleting ? <Loader2 className="w-6 h-6 animate-spin" /> : "Delete"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CallHistory;
|
||||||
@ -54,8 +54,8 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
const education = data.educationalDetails;
|
const education = data.educationalDetails;
|
||||||
const lifestyle = data.lifestyleDetails;
|
const lifestyle = data.lifestyleDetails;
|
||||||
|
|
||||||
const profileImages = personal.images && personal.images.length > 0
|
const profileImages = personal.images && personal.images.length > 0
|
||||||
? personal.images
|
? personal.images
|
||||||
: [profile.profile_picture || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"];
|
: [profile.profile_picture || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"];
|
||||||
|
|
||||||
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
|
||||||
@ -235,10 +235,10 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
|
|
||||||
const handleMessage = (e) => {
|
const handleMessage = (e) => {
|
||||||
if (e) e.stopPropagation();
|
if (e) e.stopPropagation();
|
||||||
|
|
||||||
if (profile.chat_id) {
|
if (profile.chat_id) {
|
||||||
navigate(`/chat/${profile.chat_id}`);
|
navigate(`/chat/${profile.chat_id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUserPaid) {
|
if (!isUserPaid) {
|
||||||
@ -267,16 +267,16 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
|
|
||||||
const _handleCreateChat = async () => {
|
const _handleCreateChat = async () => {
|
||||||
if (isCreatingChat) return;
|
if (isCreatingChat) return;
|
||||||
|
|
||||||
setIsChatConfirmModalOpen(false);
|
setIsChatConfirmModalOpen(false);
|
||||||
setIsCreatingChat(true);
|
setIsCreatingChat(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axiosInstance.post(API_ENDPOINTS.CHAT_CREATE, { profile_id: profile.id });
|
const response = await axiosInstance.post(API_ENDPOINTS.CHAT_CREATE, { profile_id: profile.id });
|
||||||
|
|
||||||
if (response.data?.status === true || response.data?.status === 'success') {
|
if (response.data?.status === true || response.data?.status === 'success') {
|
||||||
const newChatId = response.data?.chat_id;
|
const newChatId = response.data?.chat_id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendMessage(newChatId, "This profile has initiated a chat with you");
|
await sendMessage(newChatId, "This profile has initiated a chat with you");
|
||||||
} catch (msgErr) {
|
} catch (msgErr) {
|
||||||
@ -358,9 +358,9 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
if (!value) return "N/A";
|
if (!value) return "N/A";
|
||||||
const strValue = String(value);
|
const strValue = String(value);
|
||||||
const isUpgrade = strValue.toLowerCase().includes("upgrade");
|
const isUpgrade = strValue.toLowerCase().includes("upgrade");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`ml-3 font-semibold ${isUpgrade ? "text-[#DF1D46] underline cursor-pointer" : "text-gray-900"}`}
|
className={`ml-3 font-semibold ${isUpgrade ? "text-[#DF1D46] underline cursor-pointer" : "text-gray-900"}`}
|
||||||
onClick={() => isUpgrade && setIsUpgradeModalOpen(true)}
|
onClick={() => isUpgrade && setIsUpgradeModalOpen(true)}
|
||||||
>
|
>
|
||||||
@ -378,11 +378,11 @@ 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
|
<ReportModel
|
||||||
open={reportModalOpen}
|
open={reportModalOpen}
|
||||||
onClose={() => setReportModalOpen(false)}
|
onClose={() => setReportModalOpen(false)}
|
||||||
onSubmit={handleReportAction}
|
onSubmit={handleReportAction}
|
||||||
actionType={reportActionType}
|
actionType={reportActionType}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="max-w-[1200px] mx-auto
|
className="max-w-[1200px] mx-auto
|
||||||
@ -467,20 +467,20 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</button>
|
</button>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<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 ${profile.is_shortlisted === 1 ? 'text-green-700' : ''}`}
|
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 ${profile.is_shortlisted === 1 ? 'fill-current text-green-700' : ''}`} />
|
<Bookmark className={`w-4 h-4 ${profile.is_shortlisted === 1 ? 'fill-current text-green-700' : ''}`} />
|
||||||
{profile.is_shortlisted === 1 ? "Shortlisted" : "Shortlist"}
|
{profile.is_shortlisted === 1 ? "Shortlisted" : "Shortlist"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowMenu(false); navigate("/chat"); }}
|
onClick={() => { setShowMenu(false); navigate("/chat"); }}
|
||||||
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"
|
||||||
>
|
>
|
||||||
<MessageCircle className="w-4 h-4" /> Send Message
|
<MessageCircle className="w-4 h-4" /> Send Message
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
if (profile.is_blocked) {
|
if (profile.is_blocked) {
|
||||||
@ -493,7 +493,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
>
|
>
|
||||||
<Ban className="w-4 h-4" /> {profile.is_blocked ? "Unblock Profile" : "Block Profile"}
|
<Ban className="w-4 h-4" /> {profile.is_blocked ? "Unblock Profile" : "Block Profile"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
openReportModal('report');
|
openReportModal('report');
|
||||||
@ -526,7 +526,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{(profile.religion || profile.caste || profile.sub_caste || profile.college_name) && (
|
{(profile.religion || profile.caste || profile.sub_caste || profile.college_name) && (
|
||||||
<>
|
<>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span className="text-sm">
|
<span className="text-sm">
|
||||||
{[
|
{[
|
||||||
@ -536,29 +536,22 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
profile.college_name
|
profile.college_name
|
||||||
].filter(v => v !== "N/A" && v !== undefined).join(" / ")}
|
].filter(v => v !== "N/A" && v !== undefined).join(" / ")}
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex justify-start gap-3 mt-4">
|
<div className="flex justify-start gap-3 mt-4">
|
||||||
{profile.is_blocked ? (
|
{profile.is_send_interest_received && profile.statusReceived?.toLowerCase() === 'pending' ? (
|
||||||
<button
|
|
||||||
onClick={handleUnblock}
|
|
||||||
className="w-[fit-content] flex items-center justify-center gap-2 py-2 px-8 bg-gray-500 text-white rounded-full font-bold text-sm hover:bg-gray-600 transition-all active:scale-[0.98]"
|
|
||||||
>
|
|
||||||
<Ban size={18} /> Unblock Profile
|
|
||||||
</button>
|
|
||||||
) : profile.is_send_interest_received && profile.statusReceived?.toLowerCase() === 'pending' ? (
|
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={handleSendInterest}
|
onClick={handleSendInterest}
|
||||||
className="w-[fit-content] flex items-center justify-center gap-2 py-2 px-8 bg-[#0C8626] text-white rounded-full font-bold text-sm hover:bg-green-700 transition-all active:scale-[0.98]"
|
className="w-[fit-content] flex items-center justify-center gap-2 py-2 px-8 bg-[#0C8626] text-white rounded-full font-bold text-sm hover:bg-green-700 transition-all active:scale-[0.98]"
|
||||||
>
|
>
|
||||||
<Heart size={18} fill="white" /> Accept
|
<Heart size={18} fill="white" /> Accept
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleDecline}
|
onClick={handleDecline}
|
||||||
className="w-[fit-content] flex items-center justify-center gap-2 py-2 px-8 bg-white border border-red-200 text-red-600 rounded-full font-bold text-sm hover:bg-red-50 transition-all active:scale-[0.98]"
|
className="w-[fit-content] flex items-center justify-center gap-2 py-2 px-8 bg-white border border-red-200 text-red-600 rounded-full font-bold text-sm hover:bg-red-50 transition-all active:scale-[0.98]"
|
||||||
>
|
>
|
||||||
@ -566,7 +559,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : (!profile.is_send_interest && !profile.is_send_interest_received) ? (
|
) : (!profile.is_send_interest && !profile.is_send_interest_received) ? (
|
||||||
<button
|
<button
|
||||||
onClick={handleSendInterest}
|
onClick={handleSendInterest}
|
||||||
className="w-[fit-content] bg-[#034E08] text-white py-2.5 px-10 rounded-full hover:bg-green-800 transition-colors flex items-center justify-center gap-2 font-bold text-sm shadow-lg shadow-green-900/10"
|
className="w-[fit-content] bg-[#034E08] text-white py-2.5 px-10 rounded-full hover:bg-green-800 transition-colors flex items-center justify-center gap-2 font-bold text-sm shadow-lg shadow-green-900/10"
|
||||||
>
|
>
|
||||||
@ -574,13 +567,13 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={handleCall}
|
onClick={handleCall}
|
||||||
className="w-[fit-content] flex items-center justify-center gap-2 py-2.5 px-8 bg-white border border-green-200 text-green-700 rounded-full font-bold text-sm hover:bg-green-50 transition-all active:scale-[0.98] shadow-sm"
|
className="w-[fit-content] flex items-center justify-center gap-2 py-2.5 px-8 bg-white border border-green-200 text-green-700 rounded-full font-bold text-sm hover:bg-green-50 transition-all active:scale-[0.98] shadow-sm"
|
||||||
>
|
>
|
||||||
<Phone size={18} /> Call
|
<Phone size={18} /> Call
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleMessage}
|
onClick={handleMessage}
|
||||||
className="w-[fit-content] flex items-center justify-center gap-2 py-2.5 px-8 bg-white border border-red-200 text-[#DF1D46] rounded-full font-bold text-sm hover:bg-red-50 transition-all active:scale-[0.98] shadow-sm"
|
className="w-[fit-content] flex items-center justify-center gap-2 py-2.5 px-8 bg-white border border-red-200 text-[#DF1D46] rounded-full font-bold text-sm hover:bg-red-50 transition-all active:scale-[0.98] shadow-sm"
|
||||||
>
|
>
|
||||||
@ -720,7 +713,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
<div className="w-[100%] max-w-[1200px] mx-auto my-10 grid grid-cols-1 gap-4 md:grid-cols-2">
|
<div className="w-[100%] max-w-[1200px] mx-auto my-10 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
{/* Personal Information Section */}
|
{/* Personal Information Section */}
|
||||||
<div className="border border-gray-200 rounded-lg bg-pink-50/30">
|
<div className="border border-gray-200 rounded-lg bg-pink-50/30">
|
||||||
@ -1056,162 +1049,160 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-8">
|
<div className="my-8">
|
||||||
<div className="flex items-center gap-2 p-3 bg-pink-100">
|
<div className="flex items-center gap-2 p-3 bg-pink-100">
|
||||||
<div className="bg-white p-2 rounded-full">
|
<div className="bg-white p-2 rounded-full">
|
||||||
<UserIcon className="w-5 h-5 text-[#A70710]" />
|
<UserIcon className="w-5 h-5 text-[#A70710]" />
|
||||||
</div>
|
|
||||||
<h3 className="font-semibold text-lg">Contact Details</h3>
|
|
||||||
</div>
|
</div>
|
||||||
|
<h3 className="font-semibold text-lg">Contact Details</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="p-5 space-y-3 text-sm">
|
<div className="p-5 space-y-3 text-sm">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="text-gray-600 w-48">Phone Number</span>
|
<span className="text-gray-600 w-48">Phone Number</span>
|
||||||
<span className="text-gray-400">:</span>
|
<span className="text-gray-400">:</span>
|
||||||
<span
|
<span
|
||||||
className={`ml-3 font-semibold cursor-pointer ${
|
className={`ml-3 font-semibold cursor-pointer ${(profile.mobile || "").toLowerCase().includes("view") || (profile.mobile || "").toLowerCase().includes("upgrade")
|
||||||
(profile.mobile || "").toLowerCase().includes("view") || (profile.mobile || "").toLowerCase().includes("upgrade")
|
? "text-[#DF1D46] underline"
|
||||||
? "text-[#DF1D46] underline"
|
: "text-gray-900"
|
||||||
: "text-gray-900"
|
|
||||||
}`}
|
}`}
|
||||||
onClick={() => _handleContactInteraction('mobile')}
|
onClick={() => _handleContactInteraction('mobile')}
|
||||||
>
|
>
|
||||||
{unlockedMobile || profile.mobile || "N/A"}
|
{unlockedMobile || profile.mobile || "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="text-gray-600 w-48">Email</span>
|
<span className="text-gray-600 w-48">Email</span>
|
||||||
<span className="text-gray-400">:</span>
|
<span className="text-gray-400">:</span>
|
||||||
<span
|
<span
|
||||||
className={`ml-3 font-semibold cursor-pointer ${
|
className={`ml-3 font-semibold cursor-pointer ${(profile.email || "").toLowerCase().includes("view") || (profile.email || "").toLowerCase().includes("upgrade")
|
||||||
(profile.email || "").toLowerCase().includes("view") || (profile.email || "").toLowerCase().includes("upgrade")
|
? "text-[#DF1D46] underline"
|
||||||
? "text-[#DF1D46] underline"
|
: "text-gray-900"
|
||||||
: "text-gray-900"
|
|
||||||
}`}
|
}`}
|
||||||
onClick={() => _handleContactInteraction('email')}
|
onClick={() => _handleContactInteraction('email')}
|
||||||
>
|
>
|
||||||
{unlockedEmail || profile.email || "N/A"}
|
{unlockedEmail || profile.email || "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Educational & Professional Section */}
|
{/* Educational & Professional Section */}
|
||||||
<div className="my-8">
|
<div className="my-8">
|
||||||
<div className="flex items-center gap-2 p-3 bg-pink-100 rounded-t-lg">
|
<div className="flex items-center gap-2 p-3 bg-pink-100 rounded-t-lg">
|
||||||
<div className="bg-white p-2 rounded-full">
|
<div className="bg-white p-2 rounded-full">
|
||||||
<BookOpen className="w-5 h-5 text-[#A70710]" />
|
<BookOpen className="w-5 h-5 text-[#A70710]" />
|
||||||
</div>
|
|
||||||
<h3 className="font-semibold text-lg">Educational & Professional</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-5 space-y-3 text-sm bg-white border border-gray-200 border-t-0 rounded-b-lg">
|
|
||||||
{(education.education || safeVal(profile.education, 'education_name')) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Highest Qualification</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.education || safeVal(profile.education, 'education_name'))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(education.study_field || safeVal(profile.study_field, 'study_field_name')) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Field of Study</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.study_field || safeVal(profile.study_field, 'study_field_name'))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(profile.college_name || education.college_name) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">College Name</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(profile.college_name || education.college_name)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(education.employee_type || safeVal(profile.employee_type, 'employee_type_name')) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Employee type</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.employee_type || safeVal(profile.employee_type, 'employee_type_name'))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(education.occupation || safeVal(profile.occupation, 'occupation_name')) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Occupation</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.occupation || safeVal(profile.occupation, 'occupation_name'))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{profile.occupation_details && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Occupation Details</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
<span className="ml-3 text-gray-900">{profile.occupation_details}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(education.company_name || profile.company_name) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Organization Name</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.company_name || profile.company_name)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(profile.income_currency || education.income_currency) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Income Currency Type</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(profile.income_currency || education.income_currency)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(education.annual_income || safeVal(profile.annual_income, 'annual_income_name')) && (
|
|
||||||
<div className="flex">
|
|
||||||
<span className="text-gray-600 w-48">Annual Income</span>
|
|
||||||
<span className="text-gray-400">:</span>
|
|
||||||
{_renderValue(education.annual_income || safeVal(profile.annual_income, 'annual_income_name'))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<h3 className="font-semibold text-lg">Educational & Professional</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Work Location Section */}
|
<div className="p-5 space-y-3 text-sm bg-white border border-gray-200 border-t-0 rounded-b-lg">
|
||||||
<div className="my-8">
|
{(education.education || safeVal(profile.education, 'education_name')) && (
|
||||||
<div className="flex items-center gap-2 p-3 bg-pink-100 rounded-t-lg">
|
<div className="flex">
|
||||||
<div className="bg-white p-2 rounded-full">
|
<span className="text-gray-600 w-48">Highest Qualification</span>
|
||||||
<MapPin className="w-5 h-5 text-[#A70710]" />
|
<span className="text-gray-400">:</span>
|
||||||
|
{_renderValue(education.education || safeVal(profile.education, 'education_name'))}
|
||||||
</div>
|
</div>
|
||||||
<h3 className="font-semibold text-lg">Work Location</h3>
|
)}
|
||||||
</div>
|
{(education.study_field || safeVal(profile.study_field, 'study_field_name')) && (
|
||||||
|
<div className="flex">
|
||||||
<div className="p-5 space-y-3 text-sm bg-white border border-gray-200 border-t-0 rounded-b-lg">
|
<span className="text-gray-600 w-48">Field of Study</span>
|
||||||
{(education.work_country_name || profile.work_country_name) && (
|
<span className="text-gray-400">:</span>
|
||||||
<div className="flex">
|
{_renderValue(education.study_field || safeVal(profile.study_field, 'study_field_name'))}
|
||||||
<span className="text-gray-600 w-48">Country</span>
|
</div>
|
||||||
<span className="text-gray-400">:</span>
|
)}
|
||||||
<span className="ml-3 text-gray-900">{education.work_country_name || profile.work_country_name}</span>
|
{(profile.college_name || education.college_name) && (
|
||||||
</div>
|
<div className="flex">
|
||||||
)}
|
<span className="text-gray-600 w-48">College Name</span>
|
||||||
{(education.work_state_name || profile.work_state_name) && (
|
<span className="text-gray-400">:</span>
|
||||||
<div className="flex">
|
{_renderValue(profile.college_name || education.college_name)}
|
||||||
<span className="text-gray-600 w-48">State</span>
|
</div>
|
||||||
<span className="text-gray-400">:</span>
|
)}
|
||||||
<span className="ml-3 text-gray-900">{education.work_state_name || profile.work_state_name}</span>
|
{(education.employee_type || safeVal(profile.employee_type, 'employee_type_name')) && (
|
||||||
</div>
|
<div className="flex">
|
||||||
)}
|
<span className="text-gray-600 w-48">Employee type</span>
|
||||||
{(education.work_city || education.work_district_name || profile.work_city || profile.work_district_name) && (
|
<span className="text-gray-400">:</span>
|
||||||
<div className="flex">
|
{_renderValue(education.employee_type || safeVal(profile.employee_type, 'employee_type_name'))}
|
||||||
<span className="text-gray-600 w-48">City</span>
|
</div>
|
||||||
<span className="text-gray-400">:</span>
|
)}
|
||||||
<span className="ml-3 text-gray-900">{education.work_city || education.work_district_name || profile.work_city || profile.work_district_name}</span>
|
{(education.occupation || safeVal(profile.occupation, 'occupation_name')) && (
|
||||||
</div>
|
<div className="flex">
|
||||||
)}
|
<span className="text-gray-600 w-48">Occupation</span>
|
||||||
{(education.work_location || profile.work_location) && (
|
<span className="text-gray-400">:</span>
|
||||||
<div className="flex">
|
{_renderValue(education.occupation || safeVal(profile.occupation, 'occupation_name'))}
|
||||||
<span className="text-gray-600 w-48">Address</span>
|
</div>
|
||||||
<span className="text-gray-400">:</span>
|
)}
|
||||||
<span className="ml-3 text-gray-900">{education.work_location || profile.work_location}</span>
|
{profile.occupation_details && (
|
||||||
</div>
|
<div className="flex">
|
||||||
)}
|
<span className="text-gray-600 w-48">Occupation Details</span>
|
||||||
</div>
|
<span className="text-gray-400">:</span>
|
||||||
|
<span className="ml-3 text-gray-900">{profile.occupation_details}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(education.company_name || profile.company_name) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">Organization Name</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
{_renderValue(education.company_name || profile.company_name)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(profile.income_currency || education.income_currency) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">Income Currency Type</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
{_renderValue(profile.income_currency || education.income_currency)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(education.annual_income || safeVal(profile.annual_income, 'annual_income_name')) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">Annual Income</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
{_renderValue(education.annual_income || safeVal(profile.annual_income, 'annual_income_name'))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Work Location Section */}
|
||||||
|
<div className="my-8">
|
||||||
|
<div className="flex items-center gap-2 p-3 bg-pink-100 rounded-t-lg">
|
||||||
|
<div className="bg-white p-2 rounded-full">
|
||||||
|
<MapPin className="w-5 h-5 text-[#A70710]" />
|
||||||
|
</div>
|
||||||
|
<h3 className="font-semibold text-lg">Work Location</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-5 space-y-3 text-sm bg-white border border-gray-200 border-t-0 rounded-b-lg">
|
||||||
|
{(education.work_country_name || profile.work_country_name) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">Country</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
<span className="ml-3 text-gray-900">{education.work_country_name || profile.work_country_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(education.work_state_name || profile.work_state_name) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">State</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
<span className="ml-3 text-gray-900">{education.work_state_name || profile.work_state_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(education.work_city || education.work_district_name || profile.work_city || profile.work_district_name) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">City</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
<span className="ml-3 text-gray-900">{education.work_city || education.work_district_name || profile.work_city || profile.work_district_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{(education.work_location || profile.work_location) && (
|
||||||
|
<div className="flex">
|
||||||
|
<span className="text-gray-600 w-48">Address</span>
|
||||||
|
<span className="text-gray-400">:</span>
|
||||||
|
<span className="ml-3 text-gray-900">{education.work_location || profile.work_location}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Modals */}
|
{/* Modals */}
|
||||||
<UpgradeModal
|
<UpgradeModal
|
||||||
@ -1258,31 +1249,31 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
{/* View Contact Confirmation Modal */}
|
{/* View Contact Confirmation Modal */}
|
||||||
{isViewContactModalOpen && (
|
{isViewContactModalOpen && (
|
||||||
<div className="fixed inset-0 z-[10001] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
<div className="fixed inset-0 z-[10001] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.9 }}
|
exit={{ opacity: 0, scale: 0.9 }}
|
||||||
className="bg-white rounded-[32px] p-8 max-w-md w-full shadow-2xl text-center relative"
|
className="bg-white rounded-[32px] p-8 max-w-md w-full shadow-2xl text-center relative"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsViewContactModalOpen(false)}
|
onClick={() => setIsViewContactModalOpen(false)}
|
||||||
className="absolute top-6 left-6 text-gray-400 hover:text-gray-600 transition-colors"
|
className="absolute top-6 left-6 text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
>
|
>
|
||||||
<ChevronLeft size={24} />
|
<ChevronLeft size={24} />
|
||||||
</button>
|
</button>
|
||||||
<h3 className="text-3xl font-bold text-gray-900 mb-8 mt-2">Note</h3>
|
<h3 className="text-3xl font-bold text-gray-900 mb-8 mt-2">Note</h3>
|
||||||
<p className="text-gray-600 mb-10 text-[17px] leading-relaxed px-2">
|
<p className="text-gray-600 mb-10 text-[17px] leading-relaxed px-2">
|
||||||
You need to view this profile's contact details. If you choose to <span className="text-[#DF1D46] font-bold">"Proceed"</span> one count will be deducted from your subscription.
|
You need to view this profile's contact details. If you choose to <span className="text-[#DF1D46] font-bold">"Proceed"</span> one count will be deducted from your subscription.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4 mt-2">
|
<div className="flex gap-4 mt-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsViewContactModalOpen(false)}
|
onClick={() => setIsViewContactModalOpen(false)}
|
||||||
className="flex-1 py-4 border border-gray-100 rounded-2xl font-bold text-gray-500 hover:bg-gray-50 transition-all"
|
className="flex-1 py-4 border border-gray-100 rounded-2xl font-bold text-gray-500 hover:bg-gray-50 transition-all"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={_handleViewContact}
|
onClick={_handleViewContact}
|
||||||
className="flex-1 py-4 bg-[#DF1D46] text-white rounded-2xl font-bold hover:bg-red-700 shadow-lg shadow-red-200 transition-all"
|
className="flex-1 py-4 bg-[#DF1D46] text-white rounded-2xl font-bold hover:bg-red-700 shadow-lg shadow-red-200 transition-all"
|
||||||
>
|
>
|
||||||
Proceed
|
Proceed
|
||||||
@ -1357,7 +1348,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl font-bold text-[#0C8626] mb-2">Success!</h3>
|
<h3 className="text-2xl font-bold text-[#0C8626] mb-2">Success!</h3>
|
||||||
<p className="text-gray-500 mb-6">The contact details have been unlocked.</p>
|
<p className="text-gray-500 mb-6">The contact details have been unlocked.</p>
|
||||||
|
|
||||||
<div className="bg-gray-50 rounded-2xl p-6 mb-8 border border-gray-100 flex flex-col items-center justify-center gap-4">
|
<div className="bg-gray-50 rounded-2xl p-6 mb-8 border border-gray-100 flex flex-col items-center justify-center gap-4">
|
||||||
{(() => {
|
{(() => {
|
||||||
const mobile = unlockedMobile || (profile.mobile && !profile.mobile.toLowerCase().includes("view") ? profile.mobile : null);
|
const mobile = unlockedMobile || (profile.mobile && !profile.mobile.toLowerCase().includes("view") ? profile.mobile : null);
|
||||||
@ -1369,7 +1360,7 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
{(() => {
|
{(() => {
|
||||||
const email = unlockedEmail || (profile.email && !profile.email.toLowerCase().includes("view") ? profile.email : null);
|
const email = unlockedEmail || (profile.email && !profile.email.toLowerCase().includes("view") ? profile.email : null);
|
||||||
if (!email) return null;
|
if (!email) return null;
|
||||||
@ -1412,9 +1403,9 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
{isChatConfirmModalOpen && (() => {
|
{isChatConfirmModalOpen && (() => {
|
||||||
const currentMobile = unlockedMobile || profile.mobile || "";
|
const currentMobile = unlockedMobile || profile.mobile || "";
|
||||||
const mobileLower = currentMobile.toLowerCase();
|
const mobileLower = currentMobile.toLowerCase();
|
||||||
const isMobileVisible = currentMobile !== "" &&
|
const isMobileVisible = currentMobile !== "" &&
|
||||||
!mobileLower.includes("upgrade") &&
|
!mobileLower.includes("upgrade") &&
|
||||||
!mobileLower.includes("view contact");
|
!mobileLower.includes("view contact");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
|
||||||
@ -1424,29 +1415,29 @@ const MatrimonyProfile = ({ data, onRefresh }) => {
|
|||||||
exit={{ opacity: 0, scale: 0.95 }}
|
exit={{ opacity: 0, scale: 0.95 }}
|
||||||
className="bg-white rounded-[32px] p-8 max-w-md w-full shadow-2xl text-center relative"
|
className="bg-white rounded-[32px] p-8 max-w-md w-full shadow-2xl text-center relative"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsChatConfirmModalOpen(false)}
|
onClick={() => setIsChatConfirmModalOpen(false)}
|
||||||
className="absolute top-6 left-6 text-gray-400 hover:text-gray-600 transition-colors"
|
className="absolute top-6 left-6 text-gray-400 hover:text-gray-600 transition-colors"
|
||||||
>
|
>
|
||||||
<ChevronLeft size={24} />
|
<ChevronLeft size={24} />
|
||||||
</button>
|
</button>
|
||||||
<h3 className="text-3xl font-bold text-gray-900 mb-8 mt-2">
|
<h3 className="text-3xl font-bold text-gray-900 mb-8 mt-2">
|
||||||
{isMobileVisible ? "Ready to Chat?" : "Note"}
|
{isMobileVisible ? "Ready to Chat?" : "Note"}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-600 mb-10 text-[17px] leading-relaxed px-2">
|
<p className="text-gray-600 mb-10 text-[17px] leading-relaxed px-2">
|
||||||
{isMobileVisible
|
{isMobileVisible
|
||||||
? <>Are you ready to chat with <span className="font-bold">{currentMobile}</span>?</>
|
? <>Are you ready to chat with <span className="font-bold">{currentMobile}</span>?</>
|
||||||
: <>Starting a conversation will use <span className="text-[#DF1D46] font-bold">1 chat count</span>. Are you ready to proceed?</>}
|
: <>Starting a conversation will use <span className="text-[#DF1D46] font-bold">1 chat count</span>. Are you ready to proceed?</>}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4 mt-2">
|
<div className="flex gap-4 mt-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsChatConfirmModalOpen(false)}
|
onClick={() => setIsChatConfirmModalOpen(false)}
|
||||||
className="flex-1 py-4 border border-gray-100 rounded-2xl font-bold text-gray-500 hover:bg-gray-50 transition-all"
|
className="flex-1 py-4 border border-gray-100 rounded-2xl font-bold text-gray-500 hover:bg-gray-50 transition-all"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={_handleCreateChat}
|
onClick={_handleCreateChat}
|
||||||
className={`flex-1 py-4 text-white rounded-2xl font-bold transition-all shadow-lg ${isCreatingChat ? "bg-gray-400" : "bg-[#DF1D46] hover:bg-red-700 shadow-red-200"}`}
|
className={`flex-1 py-4 text-white rounded-2xl font-bold transition-all shadow-lg ${isCreatingChat ? "bg-gray-400" : "bg-[#DF1D46] hover:bg-red-700 shadow-red-200"}`}
|
||||||
>
|
>
|
||||||
{isCreatingChat ? "Starting..." : "Proceed"}
|
{isCreatingChat ? "Starting..." : "Proceed"}
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { Search, MoreVertical, Send, Phone, Video, Check, CheckCheck, ArrowLeft, Star, Share2, Flag, Ban, Trash2, Loader2, MessageCircle } from 'lucide-react';
|
import { Search, MoreVertical, Send, Phone, Video, Check, CheckCheck, ArrowLeft, Star, Share2, Flag, Ban, Trash2, Loader2, MessageCircle } from 'lucide-react';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import ReportModal from '../components/common/ReportModal';
|
import ReportModal from '../components/common/ReportModal';
|
||||||
import { getChatList, getChatMessages, sendMessage } from '../services/chatApi';
|
import { getChatList, getChatMessages, sendMessage, deleteChats } from '../services/chatApi';
|
||||||
|
import CallHistory from '../components/chat/CallHistory';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { useWebSocket } from '../hooks/useWebSocket';
|
import { useWebSocket } from '../hooks/useWebSocket';
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ const ChatUI = () => {
|
|||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [showChatMenu, setShowChatMenu] = useState(false);
|
const [showChatMenu, setShowChatMenu] = useState(false);
|
||||||
const [openReport, setOpenReport] = useState(false);
|
const [openReport, setOpenReport] = useState(false);
|
||||||
|
|
||||||
const [contacts, setContacts] = useState([]);
|
const [contacts, setContacts] = useState([]);
|
||||||
const [chatMessages, setChatMessages] = useState([]);
|
const [chatMessages, setChatMessages] = useState([]);
|
||||||
const [chatDetails, setChatDetails] = useState(null);
|
const [chatDetails, setChatDetails] = useState(null);
|
||||||
@ -33,7 +34,12 @@ const ChatUI = () => {
|
|||||||
const [loadingMore, setLoadingMore] = useState(false);
|
const [loadingMore, setLoadingMore] = useState(false);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [hasMore, setHasMore] = useState(true);
|
const [hasMore, setHasMore] = useState(true);
|
||||||
|
|
||||||
|
// Chat Deletion state
|
||||||
|
const [isEditContactMode, setIsEditContactMode] = useState(false);
|
||||||
|
const [selectedChats, setSelectedChats] = useState([]);
|
||||||
|
const [isDeletingChats, setIsDeletingChats] = useState(false);
|
||||||
|
|
||||||
const messagesEndRef = useRef(null);
|
const messagesEndRef = useRef(null);
|
||||||
const scrollContainerRef = useRef(null);
|
const scrollContainerRef = useRef(null);
|
||||||
const isPaginating = useRef(false);
|
const isPaginating = useRef(false);
|
||||||
@ -43,28 +49,28 @@ const ChatUI = () => {
|
|||||||
|
|
||||||
// WebSocket Integration - Listening to BOTH Notifications and the Active Chat
|
// WebSocket Integration - Listening to BOTH Notifications and the Active Chat
|
||||||
const profileId = localStorage.getItem("profile_id") || personalDetails?.id;
|
const profileId = localStorage.getItem("profile_id") || personalDetails?.id;
|
||||||
const userId = localStorage.getItem("user_id") || profileId;
|
const userId = localStorage.getItem("user_id") || profileId;
|
||||||
|
|
||||||
// To show messages INSTANTLY in bubbles, we must listen to the specific chat channel
|
// To show messages INSTANTLY in bubbles, we must listen to the specific chat channel
|
||||||
const activeChatChannel = selectedChat ? `chat-${selectedChat}` : null;
|
const activeChatChannel = selectedChat ? `chat-${selectedChat}` : null;
|
||||||
|
|
||||||
// Flutter uses both dotted and non-dotted formats for channels in some Reverb setups
|
// Flutter uses both dotted and non-dotted formats for channels in some Reverb setups
|
||||||
const wsChannels = React.useMemo(() => {
|
const wsChannels = React.useMemo(() => {
|
||||||
const channels = [];
|
const channels = [];
|
||||||
|
|
||||||
if (userId && userId !== "null") {
|
if (userId && userId !== "null") {
|
||||||
channels.push(`user-chat-notification${userId}`);
|
channels.push(`user-chat-notification${userId}`);
|
||||||
channels.push(`user-chat-notification.${userId}`); // Dotted version
|
channels.push(`user-chat-notification.${userId}`); // Dotted version
|
||||||
channels.push(`user-notification${userId}`);
|
channels.push(`user-notification${userId}`);
|
||||||
channels.push(`user-notification.${userId}`); // Dotted version
|
channels.push(`user-notification.${userId}`); // Dotted version
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chatDetails?.web_socket_channel) {
|
if (chatDetails?.web_socket_channel) {
|
||||||
channels.push(chatDetails.web_socket_channel);
|
channels.push(chatDetails.web_socket_channel);
|
||||||
} else if (activeChatChannel) {
|
} else if (activeChatChannel) {
|
||||||
channels.push(activeChatChannel);
|
channels.push(activeChatChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...new Set(channels.filter(Boolean))];
|
return [...new Set(channels.filter(Boolean))];
|
||||||
}, [userId, activeChatChannel, chatDetails?.web_socket_channel]);
|
}, [userId, activeChatChannel, chatDetails?.web_socket_channel]);
|
||||||
|
|
||||||
@ -125,11 +131,11 @@ const ChatUI = () => {
|
|||||||
|
|
||||||
const loadMoreMessages = useCallback(async () => {
|
const loadMoreMessages = useCallback(async () => {
|
||||||
if (!selectedChat || loadingMore || !hasMore) return;
|
if (!selectedChat || loadingMore || !hasMore) return;
|
||||||
|
|
||||||
setLoadingMore(true);
|
setLoadingMore(true);
|
||||||
isPaginating.current = true;
|
isPaginating.current = true;
|
||||||
const nextPage = currentPage + 1;
|
const nextPage = currentPage + 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (scrollContainerRef.current) {
|
if (scrollContainerRef.current) {
|
||||||
previousScrollHeight.current = scrollContainerRef.current.scrollHeight;
|
previousScrollHeight.current = scrollContainerRef.current.scrollHeight;
|
||||||
@ -177,11 +183,11 @@ const ChatUI = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentLen = wsMessages.length;
|
const currentLen = wsMessages.length;
|
||||||
const lastProcessed = processedMsgCount.current;
|
const lastProcessed = processedMsgCount.current;
|
||||||
|
|
||||||
if (currentLen > lastProcessed) {
|
if (currentLen > lastProcessed) {
|
||||||
const newWsMsgs = wsMessages.slice(lastProcessed);
|
const newWsMsgs = wsMessages.slice(lastProcessed);
|
||||||
console.log(`[WS-REAL-TIME] New messages: ${newWsMsgs.length} (Total: ${currentLen}, Last Processed: ${lastProcessed})`);
|
console.log(`[WS-REAL-TIME] New messages: ${newWsMsgs.length} (Total: ${currentLen}, Last Processed: ${lastProcessed})`);
|
||||||
|
|
||||||
let shouldRefreshContacts = false;
|
let shouldRefreshContacts = false;
|
||||||
let shouldRefreshMessages = false;
|
let shouldRefreshMessages = false;
|
||||||
|
|
||||||
@ -191,16 +197,16 @@ const ChatUI = () => {
|
|||||||
if (lastMsg.event === 'pusher_internal:subscription_succeeded') return;
|
if (lastMsg.event === 'pusher_internal:subscription_succeeded') return;
|
||||||
|
|
||||||
console.log(`[WS-REAL-TIME] Processing: ${lastMsg.event}`);
|
console.log(`[WS-REAL-TIME] Processing: ${lastMsg.event}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = lastMsg.data;
|
const data = lastMsg.data;
|
||||||
const parsedData = typeof data === 'string' ? JSON.parse(data) : data;
|
const parsedData = typeof data === 'string' ? JSON.parse(data) : data;
|
||||||
const msgObj = parsedData.message || parsedData.data || parsedData;
|
const msgObj = parsedData.message || parsedData.data || parsedData;
|
||||||
|
|
||||||
// LENIENT STRATEGY: If event name suggests a message or if we have data, refresh!
|
// LENIENT STRATEGY: If event name suggests a message or if we have data, refresh!
|
||||||
const isMessageEvent = lastMsg.event?.toLowerCase().includes('message') ||
|
const isMessageEvent = lastMsg.event?.toLowerCase().includes('message') ||
|
||||||
lastMsg.event?.toLowerCase().includes('chat');
|
lastMsg.event?.toLowerCase().includes('chat');
|
||||||
|
|
||||||
if (isMessageEvent || (msgObj && (msgObj.id || msgObj.message || msgObj.chat_id))) {
|
if (isMessageEvent || (msgObj && (msgObj.id || msgObj.message || msgObj.chat_id))) {
|
||||||
console.log("[WS-REAL-TIME] Match found!");
|
console.log("[WS-REAL-TIME] Match found!");
|
||||||
shouldRefreshContacts = true;
|
shouldRefreshContacts = true;
|
||||||
@ -225,7 +231,7 @@ const ChatUI = () => {
|
|||||||
|
|
||||||
if (selectedChat) {
|
if (selectedChat) {
|
||||||
shouldRefreshMessages = true;
|
shouldRefreshMessages = true;
|
||||||
|
|
||||||
// Manual injection for instant UI update
|
// Manual injection for instant UI update
|
||||||
if (msgObj && (msgObj.id || msgObj.message)) {
|
if (msgObj && (msgObj.id || msgObj.message)) {
|
||||||
setChatMessages(prev => {
|
setChatMessages(prev => {
|
||||||
@ -265,7 +271,7 @@ const ChatUI = () => {
|
|||||||
|
|
||||||
// Keep track of timers if needed, but for simplicity here we just use the count
|
// Keep track of timers if needed, but for simplicity here we just use the count
|
||||||
}
|
}
|
||||||
|
|
||||||
// ALWAYS update the ref if we have new messages, even if they were skipped/invalid
|
// ALWAYS update the ref if we have new messages, even if they were skipped/invalid
|
||||||
processedMsgCount.current = currentLen;
|
processedMsgCount.current = currentLen;
|
||||||
}
|
}
|
||||||
@ -332,7 +338,7 @@ const ChatUI = () => {
|
|||||||
// Refresh messages and contacts silently in the background
|
// Refresh messages and contacts silently in the background
|
||||||
setMessage('');
|
setMessage('');
|
||||||
fetchMessages(selectedChat, true);
|
fetchMessages(selectedChat, true);
|
||||||
fetchContacts(searchTerm, true);
|
fetchContacts(searchTerm, true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Failed to send message");
|
toast.error("Failed to send message");
|
||||||
@ -359,44 +365,77 @@ const ChatUI = () => {
|
|||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleChatSelection = (id) => {
|
||||||
|
if (selectedChats.includes(id)) {
|
||||||
|
setSelectedChats(selectedChats.filter(item => item !== id));
|
||||||
|
} else {
|
||||||
|
setSelectedChats([...selectedChats, id]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteChats = async () => {
|
||||||
|
if (selectedChats.length === 0) return;
|
||||||
|
setIsDeletingChats(true);
|
||||||
|
try {
|
||||||
|
const res = await deleteChats(selectedChats);
|
||||||
|
toast.success(res?.message || "Chats deleted successfully");
|
||||||
|
setIsEditContactMode(false);
|
||||||
|
setSelectedChats([]);
|
||||||
|
fetchContacts(searchTerm);
|
||||||
|
if (selectedChats.includes(selectedChat)) {
|
||||||
|
setSelectedChat(null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error("Failed to delete chats");
|
||||||
|
} finally {
|
||||||
|
setIsDeletingChats(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ReportModal open={openReport} onClose={() => setOpenReport(false)} />
|
<ReportModal open={openReport} onClose={() => setOpenReport(false)} />
|
||||||
|
|
||||||
<div className="w-full max-w-[1400px] mx-auto flex h-[85vh] gap-[20px] bg-gray-50 my-4">
|
<div className="w-full max-w-[1400px] mx-auto flex h-[85vh] gap-[20px] bg-gray-50 my-4">
|
||||||
{/* Sidebar - Chat List */}
|
{/* Sidebar - Chat List */}
|
||||||
<div key="chat-sidebar" className={`w-full md:w-96 bg-white border border-1 border-gray-200 rounded-[10px] flex flex-col ${
|
<div key="chat-sidebar" className={`w-full md:w-96 bg-white border border-1 border-gray-200 rounded-[10px] flex flex-col ${showChatOnMobile || showCallHistory ? 'hidden md:flex' : 'flex'
|
||||||
showChatOnMobile || showCallHistory ? 'hidden md:flex' : 'flex'
|
}`}>
|
||||||
}`}>
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="p-4 border-b border-gray-200">
|
<div className="p-4 border-b border-gray-200">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-4">
|
||||||
<h1 className="text-xl font-semibold">Chat</h1>
|
<h1 className="text-xl font-semibold">Chat</h1>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowMenu(!showMenu)}
|
onClick={() => setShowMenu(!showMenu)}
|
||||||
className="p-2 hover:bg-gray-100 rounded"
|
className="p-2 hover:bg-gray-100 rounded"
|
||||||
>
|
>
|
||||||
<MoreVertical className="w-5 h-5" />
|
<MoreVertical className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-50">
|
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-50">
|
||||||
<button
|
<button
|
||||||
onClick={handleCallHistoryClick}
|
onClick={handleCallHistoryClick}
|
||||||
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100"
|
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100"
|
||||||
>
|
>
|
||||||
<Phone className="w-4 h-4" />
|
<Phone className="w-4 h-4" />
|
||||||
<span className="text-sm">Call History</span>
|
<span className="text-sm">Call History</span>
|
||||||
</button>
|
</button>
|
||||||
<button className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3">
|
<button
|
||||||
<span className="text-sm">Clear chat</span>
|
onClick={() => {
|
||||||
|
setIsEditContactMode(!isEditContactMode);
|
||||||
|
setShowMenu(false);
|
||||||
|
if (isEditContactMode) setSelectedChats([]);
|
||||||
|
}}
|
||||||
|
className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 text-red-600"
|
||||||
|
>
|
||||||
|
<span className="text-sm">{isEditContactMode ? "Cancel Clear" : "Clear chat"}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
||||||
@ -425,11 +464,28 @@ const ChatUI = () => {
|
|||||||
contacts.map((contact, index) => (
|
contacts.map((contact, index) => (
|
||||||
<div
|
<div
|
||||||
key={`contact-${contact.id}-${index}`}
|
key={`contact-${contact.id}-${index}`}
|
||||||
onClick={() => handleChatSelect(contact.id)}
|
onClick={() => {
|
||||||
className={`flex items-center gap-3 p-4 cursor-pointer hover:bg-gray-50 border-b border-gray-100 ${
|
if (isEditContactMode) {
|
||||||
selectedChat === contact.id ? 'bg-blue-50' : ''
|
toggleChatSelection(contact.id);
|
||||||
}`}
|
} else {
|
||||||
|
handleChatSelect(contact.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`flex items-center gap-3 p-4 cursor-pointer hover:bg-gray-50 border-b border-gray-100 ${selectedChat === contact.id ? 'bg-blue-50' : ''
|
||||||
|
} ${isEditContactMode && selectedChats.includes(contact.id) ? 'bg-red-50/50' : ''}`}
|
||||||
>
|
>
|
||||||
|
{isEditContactMode && (
|
||||||
|
<div className="flex-shrink-0 mr-1">
|
||||||
|
<div className={`w-5 h-5 rounded border flex items-center justify-center transition-colors ${selectedChats.includes(contact.id)
|
||||||
|
? 'bg-[#df1d46] border-[#df1d46]'
|
||||||
|
: 'border-gray-300 bg-white'
|
||||||
|
}`}>
|
||||||
|
{selectedChats.includes(contact.id) && (
|
||||||
|
<svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7" /></svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img
|
<img
|
||||||
src={contact.profile || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"}
|
src={contact.profile || "https://www.thirukalyanam.amrithaa.net/backend/app-assets/images/portrait/small/no-image.png"}
|
||||||
@ -460,13 +516,24 @@ const ChatUI = () => {
|
|||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Delete Button for Chats */}
|
||||||
|
{isEditContactMode && selectedChats.length > 0 && (
|
||||||
|
<div className="p-4 border-t border-gray-100 bg-white">
|
||||||
|
<button
|
||||||
|
onClick={handleDeleteChats}
|
||||||
|
disabled={isDeletingChats}
|
||||||
|
className="w-full bg-[#A80000] hover:bg-red-900 text-white font-bold py-3 rounded-lg flex items-center justify-center gap-2 transition-all active:scale-[0.98]"
|
||||||
|
>
|
||||||
|
{isDeletingChats ? <Loader2 className="w-5 h-5 animate-spin" /> : "Delete Selected"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chat Area */}
|
{/* Chat Area */}
|
||||||
{selectedChat && !showCallHistory && (
|
{selectedChat && !showCallHistory && (
|
||||||
<div key="chat-main-area" className={`border border-1 border-gray-200 rounded-[10px] flex-1 flex flex-col bg-white ${
|
<div key="chat-main-area" className={`border border-1 border-gray-200 rounded-[10px] flex-1 flex flex-col bg-white ${showChatOnMobile ? 'flex' : 'hidden md:flex'
|
||||||
showChatOnMobile ? 'flex' : 'hidden md:flex'
|
}`}>
|
||||||
}`}>
|
|
||||||
{/* Chat Header */}
|
{/* Chat Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
@ -493,13 +560,13 @@ const ChatUI = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowChatMenu(!showChatMenu)}
|
onClick={() => setShowChatMenu(!showChatMenu)}
|
||||||
className="p-2 hover:bg-gray-100 rounded"
|
className="p-2 hover:bg-gray-100 rounded"
|
||||||
>
|
>
|
||||||
<MoreVertical className="w-5 h-5 text-gray-600" />
|
<MoreVertical className="w-5 h-5 text-gray-600" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showChatMenu && (
|
{showChatMenu && (
|
||||||
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-50">
|
<div className="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-50">
|
||||||
<button className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100">
|
<button className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100">
|
||||||
@ -510,7 +577,7 @@ const ChatUI = () => {
|
|||||||
<Share2 className="w-4 h-4" />
|
<Share2 className="w-4 h-4" />
|
||||||
<span className="text-sm">Share</span>
|
<span className="text-sm">Share</span>
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => setOpenReport(true)} className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100">
|
<button onClick={() => setOpenReport(true)} className="w-full px-4 py-3 text-left hover:bg-gray-50 flex items-center gap-3 border-b border-gray-100">
|
||||||
<Flag className="w-4 h-4" />
|
<Flag className="w-4 h-4" />
|
||||||
<span className="text-sm">Report</span>
|
<span className="text-sm">Report</span>
|
||||||
</button>
|
</button>
|
||||||
@ -525,7 +592,7 @@ const ChatUI = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<div
|
<div
|
||||||
ref={scrollContainerRef}
|
ref={scrollContainerRef}
|
||||||
className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50"
|
className="flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50"
|
||||||
>
|
>
|
||||||
@ -543,7 +610,7 @@ const ChatUI = () => {
|
|||||||
|
|
||||||
{hasMore && !loadingMore && chatMessages.length > 0 && (
|
{hasMore && !loadingMore && chatMessages.length > 0 && (
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={loadMoreMessages}
|
onClick={loadMoreMessages}
|
||||||
className="text-xs text-blue-600 hover:underline py-1"
|
className="text-xs text-blue-600 hover:underline py-1"
|
||||||
>
|
>
|
||||||
@ -562,11 +629,10 @@ const ChatUI = () => {
|
|||||||
className={`flex ${msg.chat_by === 'me' ? 'justify-end' : 'justify-start'}`}
|
className={`flex ${msg.chat_by === 'me' ? 'justify-end' : 'justify-start'}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`max-w-[75%] md:max-w-md px-4 py-2 rounded-2xl shadow-sm ${
|
className={`max-w-[75%] md:max-w-md px-4 py-2 rounded-2xl shadow-sm ${msg.chat_by === 'me'
|
||||||
msg.chat_by === 'me'
|
? 'bg-[#cbf5ea] text-gray-900 rounded-br-sm'
|
||||||
? 'bg-[#cbf5ea] text-gray-900 rounded-br-sm'
|
: 'bg-white text-gray-900 rounded-bl-sm'
|
||||||
: 'bg-white text-gray-900 rounded-bl-sm'
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<p className="text-sm break-words">{msg.message}</p>
|
<p className="text-sm break-words">{msg.message}</p>
|
||||||
<div className='flex gap-1 items-center justify-end mt-1'>
|
<div className='flex gap-1 items-center justify-end mt-1'>
|
||||||
@ -629,19 +695,11 @@ const ChatUI = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Call History Placeholder (Keep existing or update as needed) */}
|
{/* Call History Page */}
|
||||||
{showCallHistory && (
|
{showCallHistory && (
|
||||||
<div key="chat-call-history" className="flex-1 flex items-center justify-center bg-gray-50">
|
<div key="chat-call-history" className={`border border-1 border-gray-200 rounded-[10px] flex-1 overflow-hidden bg-[#fcfcfc] ${!showChatOnMobile ? 'hidden md:flex flex-col' : 'flex flex-col'
|
||||||
<div className="text-center">
|
}`}>
|
||||||
<h2 className="text-xl font-semibold mb-2">Call History</h2>
|
<CallHistory onBack={handleBackToList} />
|
||||||
<p className="text-gray-500">Feature coming soon</p>
|
|
||||||
<button
|
|
||||||
onClick={handleBackToList}
|
|
||||||
className="mt-4 px-6 py-2 bg-[#034E08] text-white rounded-lg"
|
|
||||||
>
|
|
||||||
Back to Chat
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -32,3 +32,45 @@ export const sendMessage = async (chatId, message) => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCallHistoryMyList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.get(`call_my_list`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching my call history:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCallHistoryOthersList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.get(`call_others_list`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching others call history:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteCallHistory = async (ids) => {
|
||||||
|
try {
|
||||||
|
const queryStr = ids.map((id, index) => `ids[${index}]=${id}`).join('&');
|
||||||
|
const response = await axiosInstance.get(`call_list_delete?${queryStr}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting call history:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteChats = async (ids) => {
|
||||||
|
try {
|
||||||
|
const queryStr = ids.map((id, index) => `ids[${index}]=${id}`).join('&');
|
||||||
|
const response = await axiosInstance.post(`chat/delete?${queryStr}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting chats:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user