@ -9,11 +9,21 @@ import NavigationButton from "@/components/Componentes/navigation-button";
import { PageBackground } from "@/components/Componentes/page-background" ;
import { PageBackground } from "@/components/Componentes/page-background" ;
import SupportSheet from "@/components/Componentes/support-sheet" ;
import SupportSheet from "@/components/Componentes/support-sheet" ;
import { useI18n } from "@/translations/provider" ;
import { useI18n } from "@/translations/provider" ;
import {
useMarriageAdvisorsQuery ,
type MarriageConsultant ,
} from "@/hooks/marriage/use-marriage-advisors" ;
import { postActionToFlutter } from "@/lib/webview-actions" ;
const FALLBACK_AVATAR = "/assets/images/Avatar Image.png" ;
export default function MarriageAdvisorsPage() {
export default function MarriageAdvisorsPage() {
const { dictionary : t } = useI18n ( ) ;
const { dictionary : t } = useI18n ( ) ;
const [ isSupportOpen , setIsSupportOpen ] = useState ( false ) ;
const [ isSupportOpen , setIsSupportOpen ] = useState ( false ) ;
const { data , isLoading , isError , refetch } = useMarriageAdvisorsQuery ( ) ;
const advisors = data ? . results ? ? [ ] ;
const mainStyle = {
const mainStyle = {
backgroundImage : ` url("/assets/images/islamic_pattern_2_2892_3864.svg") ` ,
backgroundImage : ` url("/assets/images/islamic_pattern_2_2892_3864.svg") ` ,
backgroundColor : "#F5F5F5" ,
backgroundColor : "#F5F5F5" ,
@ -21,62 +31,12 @@ export default function MarriageAdvisorsPage() {
backgroundSize : "100% auto" ,
backgroundSize : "100% auto" ,
} ;
} ;
const advisors = [
{
id : "hasti-masoudi" ,
name : t [ "Dr. Hasti Masoudi" ] || "Dr. Hasti Masoudi" ,
specialty :
t [ "Specialist in clinical psychology" ] ||
"Specialist in clinical psychology" ,
avatar : "/assets/images/female_avatar.svg" ,
rating : "3.2" ,
tagsRow1 : [
t [ "Personal Development" ] || "Personal Development" ,
t [ "Team Manager" ] || "Team Manager" ,
t [ "Science" ] || "Science" ,
] ,
tagsRow2 : [
t [ "Manager" ] || "Manager" ,
t [ "Team Manager" ] || "Team Manager" ,
t [ "Science" ] || "Science" ,
] ,
online : true ,
} ,
{
id : "ali-rezaei" ,
name : t [ "Dr. Ali Rezaei" ] || "Dr. Ali Rezaei" ,
specialty :
t [ "Family & marriage counselor" ] || "Family & marriage counselor" ,
avatar : "/assets/images/Avatar Image.png" ,
rating : "4.9" ,
tagsRow1 : [
t [ "Premarital Counselling" ] || "Premarital Counselling" ,
t [ "Conflict Resolution" ] || "Conflict Resolution" ,
] ,
tagsRow2 : [
t [ "Behavioral Therapy" ] || "Behavioral Therapy" ,
t [ "Self-Awareness" ] || "Self-Awareness" ,
] ,
online : true ,
} ,
{
id : "sara-alavi" ,
name : t [ "Dr. Sara Alavi" ] || "Dr. Sara Alavi" ,
specialty :
t [ "Relationship & intimacy coach" ] || "Relationship & intimacy coach" ,
avatar : "/assets/images/Ellipse 370.png" ,
rating : "4.7" ,
tagsRow1 : [
t [ "Emotional Health" ] || "Emotional Health" ,
t [ "Communication Skills" ] || "Communication Skills" ,
] ,
tagsRow2 : [
t [ "Relationship Coach" ] || "Relationship Coach" ,
t [ "Personal Development" ] || "Personal Development" ,
] ,
online : false ,
} ,
] ;
// Opens the native Talk consultant page; the Flutter side fetches the
// consultant by username, so only the bare username is needed. Outside the
// WebView (plain browser) postActionToFlutter is a no-op returning false.
const openConsultantPage = ( username : string ) = > {
postActionToFlutter ( "open_consultant_page" , { consultant : username } ) ;
} ;
return (
return (
< >
< >
@ -103,124 +63,49 @@ export default function MarriageAdvisorsPage() {
< / StickyHeader >
< / StickyHeader >
< section className = "px-[17px] pb-32 pt-5 flex flex-col items-center gap-4" >
< section className = "px-[17px] pb-32 pt-5 flex flex-col items-center gap-4" >
{ advisors . map ( ( advisor ) = > (
< div
key = { advisor . id }
className = "flex flex-col items-center py-[13px] gap-[13px] w-[343px] bg-white rounded-[15px] shadow-[0px_4px_12px_rgba(0,0,0,0.05)] border border-[#EBEBEB]"
>
{ /* Top Frame containing Left (Avatar + Info) and Right (Rating) and Actions */ }
< div className = "flex flex-col items-start p-0 gap-[11px] w-[317px]" >
{ /* Header Row */ }
< div className = "flex flex-row items-start justify-between w-full h-[45px]" >
{ /* Left part: Avatar + Info */ }
< div className = "flex flex-row items-center gap-2.5 h-[45px]" >
{ /* Avatar Container */ }
< div className = "relative w-[45px] h-[45px] shrink-0" >
< Image
src = { advisor . avatar }
alt = { advisor . name }
width = { 45 }
height = { 45 }
className = "w-full h-full rounded-full object-cover"
/ >
{ /* Status indicator dot */ }
{ advisor . online && (
< div className = "absolute top-0 right-0 w-[12px] h-[12px] rounded-full bg-white flex items-center justify-center select-none pointer-events-none" >
< span className = "absolute w-[10px] h-[10px] rounded-full bg-[#31CB2E] opacity-20 animate-ping" / >
< span className = "w-[6px] h-[6px] rounded-full bg-[#31CB2E]" / >
< / div >
) }
< / div >
{ /* Name + Title Info */ }
< div className = "flex flex-col items-start gap-[2px] h-[35px]" >
< h3
className = "font-bold text-[16px] leading-[20px] text-[#111111] text-start font-sans truncate w-[170px]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ advisor . name }
< / h3 >
< p
className = "font-semibold text-[10px] leading-[13px] text-[#8B8B8B] text-start font-sans truncate w-[170px]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ advisor . specialty }
< / p >
< / div >
< / div >
{ /* Right part: Rating */ }
< div className = "flex flex-row justify-center items-center gap-[4px] h-[20px] shrink-0" >
< FaStar className = "size-[14px] text-[#ECA533] shrink-0" / >
< span
className = "font-semibold text-[10px] leading-[13px] text-[#ECA533] font-sans text-center"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ advisor . rating }
< / span >
< / div >
< / div >
{ isLoading && < AdvisorsSkeleton / > }
{ /* Actions Row */ }
< div className = "flex flex-row items-center gap-[7px] h-[22px]" >
{ /* Text Contact Button */ }
< button
type = "button"
onClick = { ( ) = > setIsSupportOpen ( true ) }
className = "flex flex-row justify-center items-center px-2 py-0.5 gap-1.5 h-[22px] bg-[rgba(14,177,60,0.1)] rounded-[20px] text-[#0EB13C] font-semibold text-[10px] leading-[13px] transition-transform active:scale-95 cursor-pointer border-none"
>
< FiMessageSquare className = "size-3 text-[#0EB13C] shrink-0" / >
< span style = { { fontFamily : "'Segoe UI', sans-serif" } } >
{ t [ "Text" ] || "Text" }
< / span >
< / button >
{ /* Voice Call Contact Button */ }
< button
type = "button"
onClick = { ( ) = > setIsSupportOpen ( true ) }
className = "flex flex-row justify-center items-center px-2 py-0.5 gap-1.5 h-[22px] bg-[rgba(14,177,60,0.1)] rounded-[20px] text-[#0EB13C] font-semibold text-[10px] leading-[13px] transition-transform active:scale-95 cursor-pointer border-none"
>
< FiPhone className = "size-3 text-[#0EB13C] shrink-0" / >
< span style = { { fontFamily : "'Segoe UI', sans-serif" } } >
{ t [ "Voice Call" ] || "Voice Call" }
< / span >
< / button >
< / div >
< / div >
{ /* Divider */ }
< div className = "w-[343px] h-[1px] bg-[#EBEBEB]" / >
{ /* Tags Section */ }
< div className = "flex flex-col items-start gap-[9px] w-[317px]" >
{ /* Row 1 tags */ }
< div className = "flex flex-row items-center gap-[9px] w-full flex-wrap" >
{ advisor . tagsRow1 . map ( ( tag , index ) = > (
< span
key = { index }
className = "flex flex-row justify-center items-center py-[2px] px-[8px] bg-[#F5F5F5] rounded-[15px] text-[#8B8B8B] font-semibold text-[10px] leading-[13px] shrink-0 font-sans"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ tag }
< / span >
) ) }
< / div >
{ isError && (
< div className = "flex w-[343px] flex-col items-center gap-3 rounded-[15px] border border-[#EBEBEB] bg-white px-4 py-8 text-center shadow-[0px_4px_12px_rgba(0,0,0,0.05)]" >
< p
className = "text-[14px] leading-[20px] text-[#747474]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ t [ "Failed to load advisors." ] }
< / p >
< button
type = "button"
onClick = { ( ) = > refetch ( ) }
className = "cursor-pointer rounded-[20px] border-none bg-[rgba(14,177,60,0.1)] px-4 py-1.5 text-[12px] font-semibold text-[#0EB13C] transition-transform active:scale-95"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ t [ "Try again" ] }
< / button >
< / div >
) }
{ /* Row 2 tags */ }
< div className = "flex flex-row items-center gap-[9px] w-full flex-wrap" >
{ advisor . tagsRow2 . map ( ( tag , index ) = > (
< span
key = { index }
className = "flex flex-row justify-center items-center py-[2px] px-[8px] bg-[#F5F5F5] rounded-[15px] text-[#8B8B8B] font-semibold text-[10px] leading-[13px] shrink-0 font-sans"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ tag }
< / span >
) ) }
< / div >
< / div >
{ ! isLoading && ! isError && advisors . length === 0 && (
< div className = "flex w-[343px] flex-col items-center rounded-[15px] border border-[#EBEBEB] bg-white px-4 py-8 text-center shadow-[0px_4px_12px_rgba(0,0,0,0.05)]" >
< p
className = "text-[14px] leading-[20px] text-[#747474]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ t [ "No advisors are currently available." ] }
< / p >
< / div >
< / div >
) }
{ advisors . map ( ( advisor ) = > (
< AdvisorCard
key = { advisor . username }
advisor = { advisor }
onOpen = { ( ) = > openConsultantPage ( advisor . username ) }
onContact = { ( ) = > setIsSupportOpen ( true ) }
contactLabels = { {
text : t [ "Text" ] || "Text" ,
voice : t [ "Voice Call" ] || "Voice Call" ,
} }
/ >
) ) }
) ) }
< / section >
< / section >
< / main >
< / main >
@ -232,3 +117,199 @@ export default function MarriageAdvisorsPage() {
< / >
< / >
) ;
) ;
}
}
function AdvisorCard ( {
advisor ,
onOpen ,
onContact ,
contactLabels ,
} : {
advisor : MarriageConsultant ;
onOpen : ( ) = > void ;
onContact : ( ) = > void ;
contactLabels : { text : string ; voice : string } ;
} ) {
const topics = advisor . topics . filter ( Boolean ) ;
const rating = Number ( advisor . avg_rate ? ? 0 ) . toFixed ( 1 ) ;
const isOnline = advisor . status === "online" ;
return (
< div
role = "button"
tabIndex = { 0 }
aria - label = { advisor . fullname ? ? advisor . username }
onClick = { onOpen }
onKeyDown = { ( event ) = > {
if ( event . key === "Enter" || event . key === " " ) {
event . preventDefault ( ) ;
onOpen ( ) ;
}
} }
className = "flex w-[343px] cursor-pointer flex-col items-center gap-[13px] rounded-[15px] border border-[#EBEBEB] bg-white py-[13px] shadow-[0px_4px_12px_rgba(0,0,0,0.05)] transition-transform active:scale-[0.98]"
>
{ /* Top Frame containing Left (Avatar + Info) and Right (Rating) and Actions */ }
< div className = "flex h-auto w-[317px] flex-col items-start gap-[11px] p-0" >
{ /* Header Row */ }
< div className = "flex h-[45px] w-full flex-row items-start justify-between" >
{ /* Left part: Avatar + Info */ }
< div className = "flex h-[45px] flex-row items-center gap-2.5" >
{ /* Avatar Container */ }
< div className = "relative size-[45px] shrink-0" >
{ advisor . avatar_url ? (
// Consultant avatars come from arbitrary media hosts, which
// are not all listed in next.config remotePatterns — a plain
// img with an onError fallback keeps them rendering.
// eslint-disable-next-line @next/next/no-img-element
< img
src = { advisor . avatar_url }
alt = { advisor . fullname ? ? advisor . username }
className = "size-full rounded-full object-cover"
onError = { ( event ) = > {
event . currentTarget . src = FALLBACK_AVATAR ;
} }
/ >
) : (
< Image
src = { FALLBACK_AVATAR }
alt = { advisor . fullname ? ? advisor . username }
width = { 45 }
height = { 45 }
className = "size-full rounded-full object-cover"
/ >
) }
{ /* Status indicator dot */ }
{ isOnline && (
< div className = "pointer-events-none absolute top-0 right-0 flex size-[12px] select-none items-center justify-center rounded-full bg-white" >
< span className = "absolute size-[10px] animate-ping rounded-full bg-[#31CB2E] opacity-20" / >
< span className = "size-[6px] rounded-full bg-[#31CB2E]" / >
< / div >
) }
< / div >
{ /* Name + Title Info */ }
< div className = "flex h-[35px] flex-col items-start gap-[2px]" >
< h3
className = "w-[170px] truncate text-start font-sans text-[16px] leading-[20px] font-bold text-[#111111]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ advisor . fullname ? ? advisor . username }
< / h3 >
< p
className = "w-[170px] truncate text-start font-sans text-[10px] leading-[13px] font-semibold text-[#8B8B8B]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ advisor . slogan ? ? "" }
< / p >
< / div >
< / div >
{ /* Right part: Rating */ }
< div className = "flex h-[20px] shrink-0 flex-row items-center justify-center gap-[4px]" >
< FaStar className = "size-[14px] shrink-0 text-[#ECA533]" / >
< span
className = "text-center font-sans text-[10px] leading-[13px] font-semibold text-[#ECA533]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ rating }
< / span >
< / div >
< / div >
{ /* Actions Row */ }
< div className = "flex h-[22px] flex-row items-center gap-[7px]" >
{ /* Text Contact Button */ }
< button
type = "button"
onClick = { ( event ) = > {
event . stopPropagation ( ) ;
onContact ( ) ;
} }
className = "flex h-[22px] cursor-pointer flex-row items-center justify-center gap-1.5 rounded-[20px] border-none bg-[rgba(14,177,60,0.1)] px-2 py-0.5 text-[10px] leading-[13px] font-semibold text-[#0EB13C] transition-transform active:scale-95"
>
< FiMessageSquare className = "size-3 shrink-0 text-[#0EB13C]" / >
< span style = { { fontFamily : "'Segoe UI', sans-serif" } } >
{ contactLabels . text }
< / span >
< / button >
{ /* Voice Call Contact Button */ }
< button
type = "button"
onClick = { ( event ) = > {
event . stopPropagation ( ) ;
onContact ( ) ;
} }
className = "flex h-[22px] cursor-pointer flex-row items-center justify-center gap-1.5 rounded-[20px] border-none bg-[rgba(14,177,60,0.1)] px-2 py-0.5 text-[10px] leading-[13px] font-semibold text-[#0EB13C] transition-transform active:scale-95"
>
< FiPhone className = "size-3 shrink-0 text-[#0EB13C]" / >
< span style = { { fontFamily : "'Segoe UI', sans-serif" } } >
{ contactLabels . voice }
< / span >
< / button >
< / div >
< / div >
{ /* Divider */ }
< div className = "h-px w-[343px] bg-[#EBEBEB]" / >
{ /* Tags Section */ }
{ topics . length > 0 && (
< div className = "flex w-[317px] flex-col items-start gap-[9px]" >
< div className = "flex w-full flex-row flex-wrap items-center gap-[9px]" >
{ topics . map ( ( tag ) = > (
< span
key = { tag }
className = "flex shrink-0 flex-row items-center rounded-[15px] bg-[#F5F5F5] px-[8px] py-[2px] font-sans text-[10px] leading-[13px] font-semibold text-[#8B8B8B]"
style = { { fontFamily : "'Segoe UI', sans-serif" } }
>
{ tag }
< / span >
) ) }
< / div >
< / div >
) }
< / div >
) ;
}
function AdvisorsSkeleton() {
return (
< >
{ [ 0 , 1 , 2 ] . map ( ( index ) = > (
< div
key = { index }
className = "flex w-[343px] animate-pulse flex-col items-center gap-[13px] rounded-[15px] border border-[#EBEBEB] bg-white py-[13px] shadow-[0px_4px_12px_rgba(0,0,0,0.05)]"
>
< div className = "flex w-[317px] flex-col items-start gap-[11px]" >
< div className = "flex w-full flex-row items-start justify-between" >
< div className = "flex flex-row items-center gap-2.5" >
< div className = "size-[45px] shrink-0 rounded-full bg-[#F0F0F0]" / >
< div className = "flex flex-col gap-[6px]" >
< div className = "h-[14px] w-[150px] rounded bg-[#F0F0F0]" / >
< div className = "h-[10px] w-[110px] rounded bg-[#F0F0F0]" / >
< / div >
< / div >
< div className = "h-[12px] w-[34px] rounded bg-[#F0F0F0]" / >
< / div >
< div className = "flex flex-row gap-[7px]" >
< div className = "h-[22px] w-[64px] rounded-[20px] bg-[#F0F0F0]" / >
< div className = "h-[22px] w-[88px] rounded-[20px] bg-[#F0F0F0]" / >
< / div >
< / div >
< div className = "h-px w-[343px] bg-[#F0F0F0]" / >
< div className = "flex w-[317px] flex-col gap-[9px]" >
< div className = "flex flex-row gap-[9px]" >
< div className = "h-[17px] w-[92px] rounded-[15px] bg-[#F0F0F0]" / >
< div className = "h-[17px] w-[112px] rounded-[15px] bg-[#F0F0F0]" / >
< div className = "h-[17px] w-[72px] rounded-[15px] bg-[#F0F0F0]" / >
< / div >
< div className = "flex flex-row gap-[9px]" >
< div className = "h-[17px] w-[104px] rounded-[15px] bg-[#F0F0F0]" / >
< div className = "h-[17px] w-[84px] rounded-[15px] bg-[#F0F0F0]" / >
< / div >
< / div >
< / div >
) ) }
< / >
) ;
}