setting page api integrated
This commit is contained in:
parent
564b5ed50f
commit
65bd6c646b
@ -35,4 +35,23 @@ EDIT_FAMILY_DETAILS: "get_family_details",
|
|||||||
EDIT_LIFESTYLE_DETAILS: "get_lifestyle_details",
|
EDIT_LIFESTYLE_DETAILS: "get_lifestyle_details",
|
||||||
EDIT_PREFERED_PARTNER_DETAILS: "get_preferred_details",
|
EDIT_PREFERED_PARTNER_DETAILS: "get_preferred_details",
|
||||||
|
|
||||||
|
// delete api
|
||||||
|
|
||||||
|
DELETE_ACCOUNT: "delete_account",
|
||||||
|
PHONE_NUMBER_VISIBILITY: "get_phone_number_visibility",
|
||||||
|
UPDATE_PHONE_NUMBER_VISIBILITY: "update_phone_number_visibility",
|
||||||
|
CHAT_ALERT_NOTIFICATION:"get_chat_alert_notification",
|
||||||
|
UPDATE_CHAT_ALERT_NOTIFICATION:"update_chat_alert_notification",
|
||||||
|
PROFILE_PROTECT_API:"get_profile_protection",
|
||||||
|
UPDATE_PROFILE_PROTECT_API:"update_profile_protection",
|
||||||
|
MATCH_ALERT:"get_match_alert",
|
||||||
|
UPDATE_MATCH_ALERT:"update_match_alert",
|
||||||
|
WHO_CAN_VIEW_MESSAGE:"get_who_can_message_me",
|
||||||
|
UPDATE_WHO_CAN_VIEW_MESSAGE:"update_who_can_message_me",
|
||||||
|
CONTACT_US:"get_contact_us",
|
||||||
|
BE_SAFE_ONLINE:"get_be_safe_online",
|
||||||
|
NOTIFICATION_LIST:"notification/lists",
|
||||||
|
NOTIFICATION_COUNT:"notification/un_read_count",
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,3 +20,5 @@ export const logoutAPI = async () => {
|
|||||||
const res = await axiosInstance.post(API_ENDPOINTS.LOGOUT);
|
const res = await axiosInstance.post(API_ENDPOINTS.LOGOUT);
|
||||||
return res.data;
|
return res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
7
src/api/contact.api.js
Normal file
7
src/api/contact.api.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import axiosInstance from "./axiosInstance";
|
||||||
|
import { API_ENDPOINTS } from "./apiEndpoints";
|
||||||
|
|
||||||
|
export const getContactUs = async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.CONTACT_US);
|
||||||
|
return res.data;
|
||||||
|
};
|
||||||
7
src/api/safety.api.js
Normal file
7
src/api/safety.api.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import axiosInstance from "./axiosInstance";
|
||||||
|
import { API_ENDPOINTS } from "./apiEndpoints";
|
||||||
|
|
||||||
|
export const getBeSafeOnline = async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.BE_SAFE_ONLINE);
|
||||||
|
return res.data;
|
||||||
|
};
|
||||||
@ -27,6 +27,7 @@ import userimg from "../../assets/images/bride1.jpg"
|
|||||||
import axiosInstance, { logoutAPI } from "../../api/axiosInstance";
|
import axiosInstance, { logoutAPI } from "../../api/axiosInstance";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
import { API_ENDPOINTS } from "../../api/apiEndpoints";
|
||||||
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
const NAV_LINKS = [
|
const NAV_LINKS = [
|
||||||
// { label: "Home", path: "/" },
|
// { label: "Home", path: "/" },
|
||||||
{ label: "Matches", path: "/matches" },
|
{ label: "Matches", path: "/matches" },
|
||||||
@ -58,7 +59,7 @@ const ACCOUNT_SETTINGS = [
|
|||||||
{ label: "Logout", action: "logout" }
|
{ label: "Logout", action: "logout" }
|
||||||
];
|
];
|
||||||
|
|
||||||
const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick, activeItem }) => {
|
const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick, activeItem, badges = {} }) => {
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
const [indicatorStyle, setIndicatorStyle] = useState({});
|
const [indicatorStyle, setIndicatorStyle] = useState({});
|
||||||
const [isAnimating, setIsAnimating] = useState(false);
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
@ -125,9 +126,14 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick, activeItem })
|
|||||||
onClick={() => handleClick(index)}
|
onClick={() => handleClick(index)}
|
||||||
className={`cursor-pointer relative uppercase text-sm font-medium transition-all duration-300 pb-2 ${
|
className={`cursor-pointer relative uppercase text-sm font-medium transition-all duration-300 pb-2 ${
|
||||||
activeIndex === index ? "text-black" : "text-gray-900 hover:text-gray-600"
|
activeIndex === index ? "text-black" : "text-gray-900 hover:text-gray-600"
|
||||||
}`}
|
} flex items-center gap-1`}
|
||||||
>
|
>
|
||||||
{item}
|
{item}
|
||||||
|
{badges[item] > 0 && (
|
||||||
|
<span className="bg-red-600 text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full min-w-[18px] flex items-center justify-center">
|
||||||
|
{badges[item]}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@ -166,6 +172,16 @@ const ProfileHeader = () => {
|
|||||||
|
|
||||||
const currentLabel = currentNav?.label ?? NAV_LINKS[0].label;
|
const currentLabel = currentNav?.label ?? NAV_LINKS[0].label;
|
||||||
|
|
||||||
|
const { data: notificationData } = useQuery({
|
||||||
|
queryKey: ["notificationCount"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.NOTIFICATION_COUNT);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
enabled: !!auth,
|
||||||
|
});
|
||||||
|
|
||||||
|
const notificationCount = notificationData?.count || 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -182,11 +198,23 @@ const ProfileHeader = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteAccountMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
return await axiosInstance.delete(API_ENDPOINTS.DELETE_ACCOUNT);
|
||||||
|
},
|
||||||
|
onSuccess: (response) => {
|
||||||
|
toast.success(response?.data?.message || "Account deleted successfully");
|
||||||
|
setDeleteModalOpen(false);
|
||||||
|
logoutAPI();
|
||||||
|
navigate("/");
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error?.response?.data?.message || "Failed to delete account");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleDeleteAccount = () => {
|
const handleDeleteAccount = () => {
|
||||||
// Add your delete account logic here
|
deleteAccountMutation.mutate();
|
||||||
console.log("Account deleted");
|
|
||||||
setDeleteModalOpen(false);
|
|
||||||
navigate("/");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
@ -281,7 +309,14 @@ const ProfileHeader = () => {
|
|||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
{getNavIcon(index)}
|
{getNavIcon(index)}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={label} />
|
<ListItemText primary={
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
{label}
|
||||||
|
{label === "Notifications" && notificationCount > 0 && (
|
||||||
|
<span className="bg-red-600 text-white text-xs px-2 py-0.5 rounded-full">{notificationCount}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
} />
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
@ -324,6 +359,7 @@ const ProfileHeader = () => {
|
|||||||
items={NAV_LINKS.map(link => link.label)}
|
items={NAV_LINKS.map(link => link.label)}
|
||||||
color="#034E08"
|
color="#034E08"
|
||||||
activeItem={currentLabel}
|
activeItem={currentLabel}
|
||||||
|
badges={{ "Notifications": notificationCount }}
|
||||||
onItemClick={(item) => {
|
onItemClick={(item) => {
|
||||||
setSelectedItem(item);
|
setSelectedItem(item);
|
||||||
const link = NAV_LINKS.find(l => l.label === item);
|
const link = NAV_LINKS.find(l => l.label === item);
|
||||||
@ -402,12 +438,13 @@ const ProfileHeader = () => {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleDeleteAccount}
|
onClick={handleDeleteAccount}
|
||||||
|
disabled={deleteAccountMutation.isPending}
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: '#f44336',
|
bgcolor: '#f44336',
|
||||||
'&:hover': { bgcolor: '#d32f2f' }
|
'&:hover': { bgcolor: '#d32f2f' }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
{deleteAccountMutation.isPending ? "Deleting..." : "Delete"}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -4,10 +4,14 @@ import Tabs from '@mui/material/Tabs';
|
|||||||
import Tab from '@mui/material/Tab';
|
import Tab from '@mui/material/Tab';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Switch from '@mui/material/Switch';
|
import { Switch, Select, MenuItem } from '@mui/material';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import { Phone, MessageSquare, Shield, Bell, UserCheck, ChevronLeft } from 'lucide-react';
|
import { Phone, MessageSquare, Shield, Bell, UserCheck, ChevronLeft } from 'lucide-react';
|
||||||
|
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||||
|
import axiosInstance from '../api/axiosInstance';
|
||||||
|
import { API_ENDPOINTS } from '../api/apiEndpoints';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
function TabPanel(props) {
|
function TabPanel(props) {
|
||||||
const { children, value, index, ...other } = props;
|
const { children, value, index, ...other } = props;
|
||||||
@ -68,8 +72,33 @@ const SettingItem = ({ title, description, enabled, onToggle }) => (
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
||||||
const TabContent = ({ title, settings, states, onToggle }) => (
|
const SelectSettingItem = ({ title, description, value, onChange, options, disabled }) => (
|
||||||
<Box sx={{borderRadius:"10px", flex: 1, bgcolor: '#ebf7ff', height: { xs: 'auto', md: '100vh' }, overflow: 'auto', width:"100%", maxWidth:"1000px", margin:"0 auto" }}>
|
<Box sx={{ py: 2, borderBottom: '1px solid #e5e7eb', '&:last-child': { borderBottom: 'none' } }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
|
||||||
|
<Typography variant="body1" sx={{ fontWeight: 'bold', color: '#111827', fontSize: { xs: '0.875rem', sm: '1rem' } }}>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
|
sx={{ minWidth: 150, fontSize: { xs: '0.875rem', sm: '1rem' } }}
|
||||||
|
variant="standard"
|
||||||
|
>
|
||||||
|
{options.map(option => (
|
||||||
|
<MenuItem key={option} value={option}>{option}</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" sx={{ color: '#6b7280', lineHeight: 1.6, fontSize: { xs: '0.75rem', sm: '0.875rem' } }}>
|
||||||
|
{description}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const TabContent = ({ title, settings, states, onToggle, onSelectChange }) => (
|
||||||
|
<Box sx={{borderRadius:"10px", flex: 1, bgcolor: '#ffff', height: { xs: 'auto', md: '100vh' }, overflow: 'auto', width:"100%", maxWidth:"1000px", margin:"0 auto" }}>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -88,15 +117,28 @@ const TabContent = ({ title, settings, states, onToggle }) => (
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ px: { xs: 2, sm: 3 }, py: 2 }}>
|
<Box sx={{ px: { xs: 2, sm: 3 }, py: 2 }}>
|
||||||
{settings.map((setting, index) => (
|
{settings.map((setting, index) => {
|
||||||
<SettingItem
|
if (setting.type === 'select') {
|
||||||
key={index}
|
return (
|
||||||
title={setting.title}
|
<SelectSettingItem
|
||||||
description={setting.description}
|
key={index}
|
||||||
enabled={states[setting.key]}
|
title={setting.title}
|
||||||
onToggle={() => onToggle(setting.key)}
|
description={setting.description}
|
||||||
/>
|
value={states[setting.key]}
|
||||||
))}
|
onChange={(e) => onSelectChange(setting.key, e.target.value)}
|
||||||
|
options={setting.options}
|
||||||
|
disabled={!states.messagePermission}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (<SettingItem
|
||||||
|
key={index}
|
||||||
|
title={setting.title}
|
||||||
|
description={setting.description}
|
||||||
|
enabled={states[setting.key]}
|
||||||
|
onToggle={() => onToggle(setting.key)} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -113,16 +155,238 @@ export default function AccountSettingPage() {
|
|||||||
callProtection: true,
|
callProtection: true,
|
||||||
matchAlerts: true,
|
matchAlerts: true,
|
||||||
profileViews: false,
|
profileViews: false,
|
||||||
messagePermission: true,
|
messagePermission: false,
|
||||||
blockUnverified: false,
|
messageCategory: 'All Profile',
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChange = (event, newValue) => {
|
const handleChange = (event, newValue) => {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { data: phoneVisibilityData } = useQuery({
|
||||||
|
queryKey: ["phoneVisibility"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.PHONE_NUMBER_VISIBILITY);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (phoneVisibilityData) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
phoneVisibility: phoneVisibilityData.phone_number_visibility === 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [phoneVisibilityData]);
|
||||||
|
|
||||||
|
const phoneVisibilityMutation = useMutation({
|
||||||
|
mutationFn: async (isVisible) => {
|
||||||
|
return await axiosInstance.post(API_ENDPOINTS.UPDATE_PHONE_NUMBER_VISIBILITY, null, {
|
||||||
|
params: { phone_number_visibility: isVisible ? 1 : 0 },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
toast.success(res.data.message);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error("Failed to update phone visibility");
|
||||||
|
setSettings((prev) => ({ ...prev, phoneVisibility: !prev.phoneVisibility }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: chatAlertData } = useQuery({
|
||||||
|
queryKey: ["chatAlerts"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.CHAT_ALERT_NOTIFICATION);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (chatAlertData) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
chatAlertsNotification: chatAlertData.chat_alert_notification === 1,
|
||||||
|
chatProtection: chatAlertData.chat_protection === 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [chatAlertData]);
|
||||||
|
|
||||||
|
const chatAlertMutation = useMutation({
|
||||||
|
mutationFn: async (payload) => {
|
||||||
|
return await axiosInstance.post(API_ENDPOINTS.UPDATE_CHAT_ALERT_NOTIFICATION, null, {
|
||||||
|
params: {
|
||||||
|
chat_alert_notification: payload.chatAlertsNotification ? 1 : 0,
|
||||||
|
chat_protection: payload.chatProtection ? 1 : 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
toast.success(res.data.message);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error("Failed to update chat settings");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: profileProtectionData } = useQuery({
|
||||||
|
queryKey: ["profileProtection"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.PROFILE_PROTECT_API);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (profileProtectionData) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
protectProfilePhoto: profileProtectionData.profile_photo_protect === 1,
|
||||||
|
callProtection: profileProtectionData.call_protection === 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [profileProtectionData]);
|
||||||
|
|
||||||
|
const profileProtectionMutation = useMutation({
|
||||||
|
mutationFn: async (payload) => {
|
||||||
|
return await axiosInstance.post(
|
||||||
|
API_ENDPOINTS.UPDATE_PROFILE_PROTECT_API,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
profile_photo_protect: payload.protectProfilePhoto ? 1 : 0,
|
||||||
|
call_protection: payload.callProtection ? 1 : 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
toast.success(res.data.message);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error("Failed to update profile protection settings");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: matchAlertData } = useQuery({
|
||||||
|
queryKey: ["matchAlerts"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.MATCH_ALERT);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (matchAlertData) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
matchAlerts: matchAlertData.match_alert_preference === 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [matchAlertData]);
|
||||||
|
|
||||||
|
const matchAlertMutation = useMutation({
|
||||||
|
mutationFn: async (isEnabled) => {
|
||||||
|
return await axiosInstance.post(API_ENDPOINTS.UPDATE_MATCH_ALERT, null, {
|
||||||
|
params: { match_alert_preference: isEnabled ? 1 : 0 },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
toast.success(res.data.message);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error("Failed to update match alert settings");
|
||||||
|
setSettings((prev) => ({ ...prev, matchAlerts: !prev.matchAlerts }));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: whoCanMessageData } = useQuery({
|
||||||
|
queryKey: ["whoCanMessage"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await axiosInstance.get(API_ENDPOINTS.WHO_CAN_VIEW_MESSAGE);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (whoCanMessageData) {
|
||||||
|
setSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
messagePermission: whoCanMessageData.who_can_message === 1,
|
||||||
|
messageCategory: whoCanMessageData.who_can_message_categories || 'All Profile',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [whoCanMessageData]);
|
||||||
|
|
||||||
|
const whoCanMessageMutation = useMutation({
|
||||||
|
mutationFn: async (payload) => {
|
||||||
|
return await axiosInstance.post(API_ENDPOINTS.UPDATE_WHO_CAN_VIEW_MESSAGE, null, {
|
||||||
|
params: {
|
||||||
|
who_can_message: payload.messagePermission ? 1 : 0,
|
||||||
|
who_can_message_categories: payload.messageCategory,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: (res) => {
|
||||||
|
toast.success(res.data.message);
|
||||||
|
},
|
||||||
|
onError: (err, variables) => {
|
||||||
|
toast.error("Failed to update message settings");
|
||||||
|
// Revert optimistic update
|
||||||
|
setSettings(variables.oldSettings);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const toggleSetting = (key) => {
|
const toggleSetting = (key) => {
|
||||||
setSettings(prev => ({ ...prev, [key]: !prev[key] }));
|
if (key === "phoneVisibility") {
|
||||||
|
const newValue = !settings.phoneVisibility;
|
||||||
|
setSettings((prev) => ({ ...prev, [key]: newValue }));
|
||||||
|
phoneVisibilityMutation.mutate(newValue);
|
||||||
|
} else if (key === "chatAlertsNotification" || key === "chatProtection") {
|
||||||
|
const newValue = !settings[key];
|
||||||
|
setSettings((prev) => ({ ...prev, [key]: newValue }));
|
||||||
|
const payload = {
|
||||||
|
chatAlertsNotification: key === "chatAlertsNotification" ? newValue : settings.chatAlertsNotification,
|
||||||
|
chatProtection: key === "chatProtection" ? newValue : settings.chatProtection,
|
||||||
|
};
|
||||||
|
chatAlertMutation.mutate(payload);
|
||||||
|
} else if (key === "protectProfilePhoto" || key === "callProtection") {
|
||||||
|
const newValue = !settings[key];
|
||||||
|
setSettings((prev) => ({ ...prev, [key]: newValue }));
|
||||||
|
const payload = {
|
||||||
|
protectProfilePhoto:
|
||||||
|
key === "protectProfilePhoto" ? newValue : settings.protectProfilePhoto,
|
||||||
|
callProtection:
|
||||||
|
key === "callProtection" ? newValue : settings.callProtection,
|
||||||
|
};
|
||||||
|
profileProtectionMutation.mutate(payload);
|
||||||
|
} else if (key === "matchAlerts") {
|
||||||
|
const newValue = !settings.matchAlerts;
|
||||||
|
setSettings((prev) => ({ ...prev, [key]: newValue }));
|
||||||
|
matchAlertMutation.mutate(newValue);
|
||||||
|
} else if (key === 'messagePermission') {
|
||||||
|
const oldSettings = { ...settings };
|
||||||
|
const newValue = !settings.messagePermission;
|
||||||
|
setSettings(prev => ({ ...prev, [key]: newValue }));
|
||||||
|
whoCanMessageMutation.mutate({
|
||||||
|
messagePermission: newValue,
|
||||||
|
messageCategory: settings.messageCategory,
|
||||||
|
oldSettings
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSettings((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectChange = (key, value) => {
|
||||||
|
const oldSettings = { ...settings };
|
||||||
|
setSettings(prev => ({ ...prev, [key]: value }));
|
||||||
|
whoCanMessageMutation.mutate({
|
||||||
|
messagePermission: settings.messagePermission,
|
||||||
|
messageCategory: value,
|
||||||
|
oldSettings
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
@ -183,14 +447,14 @@ export default function AccountSettingPage() {
|
|||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
key: 'matchAlerts',
|
key: 'matchAlerts',
|
||||||
title: 'Match Alert Notifications',
|
title: 'Match Alerts Preferences',
|
||||||
description: 'Receive notifications when new profiles match your preferences and requirements.',
|
description: 'Match Alerts Preferences content" typically refers to the various settings within an application (e.g., sports, finance, or business software)',
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'profileViews',
|
|
||||||
title: 'Profile View Alerts',
|
|
||||||
description: 'Get notified when someone views your profile or shows interest in connecting with you.',
|
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// key: 'profileViews',
|
||||||
|
// title: 'Profile View Alerts',
|
||||||
|
// description: 'Get notified when someone views your profile or shows interest in connecting with you.',
|
||||||
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -201,14 +465,16 @@ export default function AccountSettingPage() {
|
|||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
key: 'messagePermission',
|
key: 'messagePermission',
|
||||||
title: 'Message Permissions',
|
title: 'Who Can Message Me?',
|
||||||
description: 'Control who can send you messages. You can restrict messages to only verified profiles or profiles that match your preferences.',
|
description: 'Control who is able to send you messages on the platform.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'blockUnverified',
|
key: 'messageCategory',
|
||||||
title: 'Block Unverified Profiles',
|
title: 'Allow messages from',
|
||||||
description: 'Automatically block messages from profiles that haven\'t completed verification process.',
|
description: 'Choose which category of users can message you.',
|
||||||
},
|
type: 'select',
|
||||||
|
options: ['All Profile', 'Matches', 'Premium Users']
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -219,7 +485,7 @@ export default function AccountSettingPage() {
|
|||||||
minHeight: '90vh', marginTop:"50px", marginBottom:"50px" }}>
|
minHeight: '90vh', marginTop:"50px", marginBottom:"50px" }}>
|
||||||
{/* Desktop: Vertical Sidebar */}
|
{/* Desktop: Vertical Sidebar */}
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<Box sx={{ width: 300, borderRadius:"10px" ,background:"#ffeac9", borderRight: '1px solid #e5e7eb', display: { xs: 'none', md: 'block' } }}>
|
<Box sx={{ width: 300, borderRadius:"10px" ,background:"#f2f2f2", borderRight: '1px solid #e5e7eb', display: { xs: 'none', md: 'block' } }}>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
position: 'sticky',
|
position: 'sticky',
|
||||||
top: 0,
|
top: 0,
|
||||||
@ -338,6 +604,7 @@ export default function AccountSettingPage() {
|
|||||||
settings={tab.settings}
|
settings={tab.settings}
|
||||||
states={settings}
|
states={settings}
|
||||||
onToggle={toggleSetting}
|
onToggle={toggleSetting}
|
||||||
|
onSelectChange={handleSelectChange}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,109 +1,116 @@
|
|||||||
import { useState } from 'react';
|
import React from 'react';
|
||||||
import contactimg from "../assets/images/contactimg.jpg";
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { getContactUs } from '../api/contact.api';
|
||||||
import LazyImage from '../components/common/LazyImage';
|
import LazyImage from '../components/common/LazyImage';
|
||||||
|
|
||||||
import InstagramIcon from '@mui/icons-material/Instagram';
|
import InstagramIcon from '@mui/icons-material/Instagram';
|
||||||
import FacebookIcon from '@mui/icons-material/Facebook';
|
import FacebookIcon from '@mui/icons-material/Facebook';
|
||||||
import TwitterIcon from '@mui/icons-material/Twitter';
|
import SvgIcon from '@mui/material/SvgIcon';
|
||||||
import CloseIcon from '@mui/icons-material/Close'; // using as X icon
|
import { Phone, Mail, ChevronRight } from 'lucide-react';
|
||||||
import SvgIcon from "@mui/material/SvgIcon";
|
|
||||||
|
|
||||||
function XIcon(props) {
|
const XIcon = (props) => (
|
||||||
return (
|
<SvgIcon {...props}>
|
||||||
<SvgIcon {...props} viewBox="0 0 24 24">
|
<path d="M18.3 2H21L13.4 10.5L22 22H15.6L10.7 15.7L5 22H2L9.9 13L2 2H8.5L13.9 8.8L18.3 2Z" />
|
||||||
<path d="M18.3 2H21L13.4 10.5L22 22H15.6L10.7 15.7L5 22H2L9.9 13L2 2H8.5L13.9 8.8L18.3 2Z" />
|
</SvgIcon>
|
||||||
</SvgIcon>
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ContactUsPage = () => {
|
const ContactUsPage = () => {
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: ['contactUs'],
|
||||||
|
queryFn: getContactUs,
|
||||||
|
});
|
||||||
|
|
||||||
|
const contact = data || {};
|
||||||
|
|
||||||
|
const socialMedia = [
|
||||||
|
{
|
||||||
|
name: "Instagram",
|
||||||
|
icon: <InstagramIcon fontSize="large" />,
|
||||||
|
color: "from-purple-500 to-[#d93f87]",
|
||||||
|
url: contact.instagram_url
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Facebook",
|
||||||
|
icon: <FacebookIcon fontSize="large" />,
|
||||||
|
color: "from-blue-900 to-blue-800",
|
||||||
|
url: contact.facebook_url
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X",
|
||||||
|
icon: <XIcon fontSize="large" />,
|
||||||
|
color: "from-gray-800 to-black",
|
||||||
|
url: contact.x_url
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
const socialLinks = [
|
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
||||||
{ name: "Instagram", icon: <InstagramIcon fontSize="large" />, color: "from-purple-500 to-[#d93f87]", url: "#" },
|
}
|
||||||
{ name: "Facebook", icon: <FacebookIcon fontSize="large" />, color: "from-blue-900 to-blue-800", url: "#" },
|
|
||||||
{ name: "Twitter", icon: <TwitterIcon fontSize="large" />, color: "from-blue-400 to-blue-500", url: "#" },
|
|
||||||
{ name: "X", icon: <XIcon fontSize="large" />, color: "from-gray-800 to-black", url: "#" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen my-8">
|
<div className="min-h-screen my-8">
|
||||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-2 items-center' style={{justifyItems:"center"}}>
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 items-center" style={{ justifyItems: "center" }}>
|
||||||
|
<LazyImage
|
||||||
|
src={contact.image}
|
||||||
|
className=""
|
||||||
|
alt="Contact Us"
|
||||||
|
/>
|
||||||
|
|
||||||
<LazyImage src={contactimg} className=""/>
|
<div className="max-w-md mx-auto px-4 py-8">
|
||||||
|
<div className="text-center mb-8">
|
||||||
|
<h2 className="text-2xl font-bold text-[#034E08] mb-3">{contact.title}</h2>
|
||||||
|
<p className="text-gray-600 text-sm leading-relaxed">
|
||||||
|
{contact.content}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="max-w-md mx-auto px-4 py-8">
|
<div className="space-y-4 mb-8">
|
||||||
|
<a href={`tel:${contact.mobile}`} className="block bg-white rounded-2xl shadow-md hover:shadow-xl transition-shadow p-5 border border-gray-100">
|
||||||
{/* Get in touch section */}
|
<div className="flex items-center">
|
||||||
<div className="text-center mb-8">
|
<div className="w-12 h-12 bg-[#A70710] rounded-xl flex items-center justify-center mr-4 shadow-lg">
|
||||||
<h2 className="text-2xl font-bold text-[#034E08] mb-3">Get in touch</h2>
|
<Phone className="w-6 h-6 text-white" />
|
||||||
<p className="text-gray-600 text-sm leading-relaxed">
|
</div>
|
||||||
If you have inquiries get in touch with us we'll happy to help you
|
<div className="flex-1">
|
||||||
</p>
|
<p className="text-[14px] text-gray-500 mb-1">Call us</p>
|
||||||
</div>
|
<p className="text-base font-semibold text-gray-800">{contact.mobile}</p>
|
||||||
|
</div>
|
||||||
{/* Contact Information Cards */}
|
<ChevronRight className="w-5 h-5 text-gray-400" />
|
||||||
<div className="space-y-4 mb-8">
|
|
||||||
{/* Phone Card */}
|
|
||||||
<a href="tel:9345656442" className="block bg-white rounded-2xl shadow-md hover:shadow-xl transition-shadow p-5 border border-gray-100">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="w-12 h-12 bg-[#A70710] rounded-xl flex items-center justify-center mr-4 shadow-lg">
|
|
||||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
</a>
|
||||||
<p className="text-[14px] text-gray-500 mb-1">Call us</p>
|
|
||||||
<p className="text-base font-semibold text-gray-800">93456 56442</p>
|
<a href={`mailto:${contact.email}`} className="block bg-white rounded-2xl shadow-md hover:shadow-xl transition-shadow p-5 border border-gray-100">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="w-12 h-12 bg-[#A70710] rounded-xl flex items-center justify-center mr-4 shadow-lg">
|
||||||
|
<Mail className="w-6 h-6 text-white" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-[14px] text-gray-500 mb-1">Email us</p>
|
||||||
|
<p className="text-base font-semibold text-gray-800">{contact.email}</p>
|
||||||
|
</div>
|
||||||
|
<ChevronRight className="w-5 h-5 text-gray-400" />
|
||||||
</div>
|
</div>
|
||||||
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</a>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
</div>
|
||||||
</svg>
|
|
||||||
|
<div className="mb-8">
|
||||||
|
<h3 className="text-lg font-bold text-gray-800 mb-4 text-center">Social Media</h3>
|
||||||
|
<div className="flex justify-center gap-4">
|
||||||
|
{socialMedia.map((item, index) => (
|
||||||
|
item.url && (
|
||||||
|
<a
|
||||||
|
key={index}
|
||||||
|
href={item.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className={`w-14 h-14 bg-gradient-to-br ${item.color} rounded-2xl flex items-center justify-center text-white text-2xl shadow-lg hover:scale-110 transition-transform`}
|
||||||
|
>
|
||||||
|
{item.icon}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</div>
|
||||||
|
|
||||||
{/* Email Card */}
|
|
||||||
<a href="mailto:ddsmile.03@gmailcom" className="block bg-white rounded-2xl shadow-md hover:shadow-xl transition-shadow p-5 border border-gray-100">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div className="w-12 h-12 bg-[#A70710] rounded-xl flex items-center justify-center mr-4 shadow-lg">
|
|
||||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<p className="text-[14px] text-gray-500 mb-1">Email us</p>
|
|
||||||
<p className="text-base font-semibold text-gray-800">ddsmile.03@gmailcom</p>
|
|
||||||
</div>
|
|
||||||
<svg className="w-5 h-5 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>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Social Media Section */}
|
|
||||||
<div className="mb-8">
|
|
||||||
<h3 className="text-lg font-bold text-gray-800 mb-4 text-center">Social Media</h3>
|
|
||||||
<div className="flex justify-center gap-4">
|
|
||||||
{socialLinks.map((social, idx) => (
|
|
||||||
<a
|
|
||||||
key={idx}
|
|
||||||
href={social.url}
|
|
||||||
className={`w-14 h-14 bg-gradient-to-br ${social.color} rounded-2xl flex items-center justify-center text-white text-2xl shadow-lg hover:scale-110 transition-transform`}
|
|
||||||
>
|
|
||||||
{social.icon}
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,102 +1,22 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { Tabs, Tab, IconButton, Menu, MenuItem } from '@mui/material';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { MoreVert, Delete } from '@mui/icons-material';
|
import axiosInstance from '../api/axiosInstance';
|
||||||
|
import { API_ENDPOINTS } from '../api/apiEndpoints';
|
||||||
|
import { Notifications as NotificationsIcon } from '@mui/icons-material';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
const NotificationPage = () => {
|
const NotificationPage = () => {
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const navigate = useNavigate();
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
|
||||||
const [selectedNotification, setSelectedNotification] = useState(null);
|
|
||||||
|
|
||||||
const [notifications, setNotifications] = useState([
|
const { data: notificationData, isLoading, isError } = useQuery({
|
||||||
{
|
queryKey: ['notifications'],
|
||||||
id: 1,
|
queryFn: async () => {
|
||||||
type: 'message',
|
const res = await axiosInstance.get(API_ENDPOINTS.NOTIFICATION_LIST);
|
||||||
user: 'Thangavel',
|
return res.data;
|
||||||
avatar: 'https://i.pravatar.cc/150?img=1',
|
|
||||||
message: 'has sent you a message',
|
|
||||||
time: '1d',
|
|
||||||
action: 'View message',
|
|
||||||
category: 'all'
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
id: 2,
|
|
||||||
type: 'profile',
|
|
||||||
user: 'Thangavel',
|
|
||||||
avatar: 'https://i.pravatar.cc/150?img=2',
|
|
||||||
message: 'and 3 others have viewed your profile',
|
|
||||||
time: '1d',
|
|
||||||
action: 'See who viewed',
|
|
||||||
category: 'interactions'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
type: 'interest',
|
|
||||||
user: 'Parthiban',
|
|
||||||
avatar: 'https://i.pravatar.cc/150?img=3',
|
|
||||||
message: 'has sent you an interest',
|
|
||||||
time: '1d',
|
|
||||||
action: 'View interest',
|
|
||||||
category: 'urgent'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
type: 'message',
|
|
||||||
user: 'Rajesh',
|
|
||||||
avatar: 'https://i.pravatar.cc/150?img=4',
|
|
||||||
message: 'replied to your message',
|
|
||||||
time: '2d',
|
|
||||||
action: 'View message',
|
|
||||||
category: 'all'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
type: 'profile',
|
|
||||||
user: 'Priya',
|
|
||||||
avatar: 'https://i.pravatar.cc/150?img=5',
|
|
||||||
message: 'viewed your profile',
|
|
||||||
time: '2d',
|
|
||||||
action: 'See profile',
|
|
||||||
category: 'interactions'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
type: 'interest',
|
|
||||||
user: 'Kumar',
|
|
||||||
avatar: 'https://i.pravatar.cc/150?img=6',
|
|
||||||
message: 'accepted your interest',
|
|
||||||
time: '3d',
|
|
||||||
action: 'View profile',
|
|
||||||
category: 'urgent'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleTabChange = (event, newValue) => {
|
const notifications = notificationData?.data || [];
|
||||||
setActiveTab(newValue);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMenuOpen = (event, notificationId) => {
|
|
||||||
setAnchorEl(event.currentTarget);
|
|
||||||
setSelectedNotification(notificationId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMenuClose = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
setSelectedNotification(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
setNotifications(notifications.filter(n => n.id !== selectedNotification));
|
|
||||||
handleMenuClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFilteredNotifications = () => {
|
|
||||||
if (activeTab === 0) return notifications;
|
|
||||||
if (activeTab === 1) return notifications.filter(n => n.category === 'interactions');
|
|
||||||
if (activeTab === 2) return notifications.filter(n => n.category === 'urgent');
|
|
||||||
return notifications;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredNotifications = getFilteredNotifications();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen my-10">
|
<div className="min-h-screen my-10">
|
||||||
@ -106,119 +26,50 @@ const NotificationPage = () => {
|
|||||||
<h1 className="text-2xl text-center font-semibold text-gray-900">Notifications</h1>
|
<h1 className="text-2xl text-center font-semibold text-gray-900">Notifications</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tabs */}
|
|
||||||
<div className="max-w-[350px] w-full mx-auto " >
|
|
||||||
<Tabs
|
|
||||||
value={activeTab}
|
|
||||||
onChange={handleTabChange}
|
|
||||||
sx={{
|
|
||||||
'& .MuiTab-root': {
|
|
||||||
textTransform: 'none',
|
|
||||||
fontSize: '15px',
|
|
||||||
fontWeight: 500,
|
|
||||||
minWidth: 'auto',
|
|
||||||
px: 3,
|
|
||||||
border:"1px solid #074201ff ",
|
|
||||||
display:"flex",
|
|
||||||
gap:"10px",
|
|
||||||
flexWrap:"wrap",
|
|
||||||
},
|
|
||||||
'& .Mui-selected': {
|
|
||||||
color: '#fcfcfcff !important',
|
|
||||||
backgroundColor: '#074201ff',
|
|
||||||
},
|
|
||||||
'& .MuiTabs-indicator': {
|
|
||||||
backgroundColor: '#074201ff',
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Tab label="All" />
|
|
||||||
<Tab label="Interactions" sx={{marginLeft:"10px"}} />
|
|
||||||
<Tab label="Urgent" sx={{marginLeft:"10px"}}/>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Notification List */}
|
{/* Notification List */}
|
||||||
<div className="">
|
<div className="max-w-3xl mx-auto">
|
||||||
{filteredNotifications.length === 0 ? (
|
{isLoading ? (
|
||||||
|
<div className="px-4 py-12 text-center text-gray-500">Loading notifications...</div>
|
||||||
|
) : isError ? (
|
||||||
|
<div className="px-4 py-12 text-center text-red-500">Failed to load notifications.</div>
|
||||||
|
) : notifications.length === 0 ? (
|
||||||
<div className="px-4 py-12 text-center text-gray-500">
|
<div className="px-4 py-12 text-center text-gray-500">
|
||||||
No notifications found
|
No notifications found
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<div className="space-y-2">
|
||||||
<div className="px-4 py-3 bg-[#edfffa] my-10">
|
{notifications.map((notification) => (
|
||||||
<h2 className="text-[18px] font-semibold text-gray-700">Yesterday</h2>
|
|
||||||
</div>
|
|
||||||
{filteredNotifications.map((notification) => (
|
|
||||||
<div
|
<div
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
className="px-4 py-4 hover:bg-gray-50 transition-colors bg-[#fff5ed] border-b border-[#A70710]"
|
className="px-4 py-4 hover:bg-gray-50 transition-colors bg-white border-b border-gray-200 cursor-pointer"
|
||||||
|
onClick={() => notification.page && navigate(`/${notification.page}`)}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3 ">
|
<div className="flex items-start gap-3 ">
|
||||||
{/* Avatar */}
|
{/* Icon */}
|
||||||
<img
|
<div className="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center flex-shrink-0">
|
||||||
src={notification.avatar}
|
<NotificationsIcon className="text-green-700" />
|
||||||
alt={notification.user}
|
</div>
|
||||||
className="w-12 h-12 rounded-full object-cover flex-shrink-0"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm text-gray-900">
|
<p className="text-sm text-gray-900">
|
||||||
<span className="font-semibold">{notification.user}</span>{' '}
|
<span className="font-semibold">{notification.tittle}</span>
|
||||||
<span className="text-gray-700">{notification.message}</span>
|
</p>
|
||||||
|
<p className="text-sm text-gray-600 mt-1">
|
||||||
|
{notification.description}
|
||||||
</p>
|
</p>
|
||||||
<button className="mt-2 px-4 py-1.5 text-sm font-medium text-[#A70710] border-1 border-[#A70710] rounded-full hover:bg-[#A70710] hover:text-[#fff] transition-colors">
|
|
||||||
{notification.action}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Time and Menu */}
|
{/* Time */}
|
||||||
<div className="flex items-start gap-2 flex-shrink-0">
|
<div className="flex items-start gap-2 flex-shrink-0">
|
||||||
<span className="text-xs text-gray-500 mt-1">{notification.time}</span>
|
<span className="text-xs text-gray-500 mt-1 whitespace-nowrap">{notification.created_at}</span>
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
onClick={(e) => handleMenuOpen(e, notification.id)}
|
|
||||||
sx={{
|
|
||||||
color: '#6b7280',
|
|
||||||
'&:hover': { backgroundColor: '#f3f4f6' }
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MoreVert fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Delete Menu */}
|
|
||||||
<Menu
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
open={Boolean(anchorEl)}
|
|
||||||
onClose={handleMenuClose}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: 'top',
|
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem
|
|
||||||
onClick={handleDelete}
|
|
||||||
sx={{
|
|
||||||
color: '#ef4444',
|
|
||||||
'&:hover': { backgroundColor: '#fee2e2' }
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Delete fontSize="small" sx={{ mr: 1 }} />
|
|
||||||
Delete
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,41 +1,26 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ArrowBackIosNew, Security, Report, Lock, Psychology } from '@mui/icons-material';
|
import { Security, Psychology } from '@mui/icons-material';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import LazyImage from '../components/common/LazyImage';
|
import LazyImage from '../components/common/LazyImage';
|
||||||
import safegirl from "../assets/images/safegirl.jpg";
|
import { useQuery } from '@tanstack/react-query';
|
||||||
const dummyImage = "https://images.unsplash.com/photo-1594736797933-d0501ba2fe65?w=800&q=80&fit=crop";
|
import { getBeSafeOnline } from '../api/safety.api';
|
||||||
|
|
||||||
export default function SafetyCentre() {
|
export default function SafetyCentre() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const safetyTips = [
|
const { data, isLoading } = useQuery({
|
||||||
{
|
queryKey: ['beSafeOnline'],
|
||||||
icon: <Security className="text-3xl" />,
|
queryFn: getBeSafeOnline,
|
||||||
title: "Safety Tips",
|
});
|
||||||
color: "red",
|
|
||||||
desc: "Follow these practical guidelines to protect yourself while connecting with potential Matches."
|
if (isLoading) {
|
||||||
},
|
return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
|
||||||
{
|
}
|
||||||
icon: <Report className="text-3xl" />,
|
|
||||||
title: "Report a Profile",
|
const safetyData = data?.data || [];
|
||||||
color: "orange",
|
const heroData = safetyData.length > 0 ? safetyData[0] : null;
|
||||||
desc: "Report any suspicious or inappropriate behavior immediately—especially requests for money, unsolicited contact, or blackmail threats."
|
const tipsData = safetyData.length > 1 ? safetyData.slice(1) : [];
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <Lock className="text-3xl" />,
|
|
||||||
title: "Privacy Settings",
|
|
||||||
color: "pink",
|
|
||||||
desc: "Manage who sees your photos, contact info, & profile and always be in control."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <Psychology className="text-3xl" />,
|
|
||||||
title: "Mental Wellbeing",
|
|
||||||
color: "blue",
|
|
||||||
desc: "Here's how you can prioritize your mental well-being while making this life-changing decision."
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen relative z-20">
|
<div className="min-h-screen relative z-20">
|
||||||
@ -54,52 +39,38 @@ export default function SafetyCentre() {
|
|||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
|
|
||||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-2 my-4'>
|
{heroData && (
|
||||||
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-2 my-4'>
|
||||||
<section className="md:px-6 pt-8 pb-10">
|
<section className="md:px-6 pt-8 pb-10">
|
||||||
<h2 className="text-3xl font-bold text-gray-900 mb-4">Safety Centre</h2>
|
<h2 className="text-3xl font-bold text-gray-900 mb-4" style={{ color: heroData.title_color }}>
|
||||||
<p className="text-xl text-gray-600 text-justify leading-relaxed max-w-[900px]">
|
{heroData.title}
|
||||||
Your safety matters deeply at <span className="font-bold text-red-600">Thirukalyanam</span>.
|
</h2>
|
||||||
Our team works with advanced tools to ensure your matchmaking journey remains secure and safe.
|
<p className="text-xl text-gray-600 text-justify leading-relaxed max-w-[900px]">
|
||||||
</p>
|
{heroData.content}
|
||||||
</section>
|
</p>
|
||||||
<LazyImage src={safegirl} className="rounded-tl-[50px] rounded-tr-[0px] rounded-bl-[50px] rounded-br-[50px]"/>
|
</section>
|
||||||
|
<LazyImage src={heroData.image} className="rounded-tl-[50px] rounded-tr-[0px] rounded-bl-[50px] rounded-br-[50px]"/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Safety Tips Grid */}
|
{/* Safety Tips Grid */}
|
||||||
<section className="px-6 py-12 max-w-[1400px] mx-auto space-y-4">
|
<section className="px-6 py-12 max-w-[1400px] mx-auto space-y-4">
|
||||||
{safetyTips.map((tip, index) => (
|
{tipsData.map((tip, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={index}
|
key={index}
|
||||||
initial={{ opacity: 0, x: -50 }}
|
initial={{ opacity: 0, x: -50 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
transition={{ delay: index * 0.15 }}
|
transition={{ delay: index * 0.15 }}
|
||||||
|
|
||||||
className="bg-white rounded-3xl p-8 border border-gray-100 flex items-start gap-6"
|
className="bg-white rounded-3xl p-8 border border-gray-100 flex items-start gap-6"
|
||||||
>
|
>
|
||||||
<div className={`p-4 rounded-2xl bg-gradient-to-br ${
|
<div className="p-4 rounded-2xl bg-gray-50 shadow-lg">
|
||||||
tip.color === "red" ? "from-red-500 to-rose-600" :
|
<LazyImage src={tip.icon} className="w-10 h-10 object-contain" />
|
||||||
tip.color === "orange" ? "from-orange-500 to-amber-600" :
|
|
||||||
tip.color === "pink" ? "from-pink-500 to-rose-500" :
|
|
||||||
"from-blue-500 to-indigo-600"
|
|
||||||
} text-white shadow-lg`}>
|
|
||||||
{tip.icon}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h3 className={`text-2xl font-bold mb-3 ${
|
<h3 className="text-2xl font-bold mb-3" style={{ color: tip.title_color }}>
|
||||||
tip.color === "red" ? "text-red-600" :
|
|
||||||
tip.color === "orange" ? "text-orange-600" :
|
|
||||||
tip.color === "pink" ? "text-pink-600" :
|
|
||||||
"text-blue-600"
|
|
||||||
}`}>
|
|
||||||
{tip.title}
|
{tip.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-gray-700 text-lg leading-relaxed">{tip.desc}</p>
|
<p className="text-gray-700 text-lg leading-relaxed">{tip.content}</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user