Browse Source

feat(marriage): implement dynamic marriage advisors loading

Replace static advisor data with a dynamic fetch from the API using
React Query. This includes adding a new custom hook for marriage
advisors, updating query keys, and integrating with the Flutter
webview to allow opening consultant pages natively.

- Add `useMarriageAdvisorsQuery` hook
- Implement `getMarriageConsultants` API call
- Update `MarriageAdvisorsPage` to use fetched data
- Add error and empty state translations across all locales
- Integrate `postActionToFlutter` for native navigation
Dev
mortezaei 1 week ago
parent
commit
3259a3ac94
  1. 423
      src/app/marriage-advisors/page.tsx
  2. 1
      src/hooks/marriage/query-keys.ts
  3. 58
      src/hooks/marriage/use-marriage-advisors.ts
  4. 5
      src/translations/locales/ar.json
  5. 5
      src/translations/locales/az.json
  6. 5
      src/translations/locales/bn.json
  7. 5
      src/translations/locales/da.json
  8. 5
      src/translations/locales/de.json
  9. 5
      src/translations/locales/en.json
  10. 5
      src/translations/locales/es.json
  11. 5
      src/translations/locales/fa.json
  12. 5
      src/translations/locales/fr.json
  13. 5
      src/translations/locales/gu.json
  14. 5
      src/translations/locales/ha.json
  15. 5
      src/translations/locales/he.json
  16. 5
      src/translations/locales/hi.json
  17. 5
      src/translations/locales/id.json
  18. 5
      src/translations/locales/ks.json
  19. 5
      src/translations/locales/pt.json
  20. 5
      src/translations/locales/ru.json
  21. 5
      src/translations/locales/sw.json
  22. 5
      src/translations/locales/tg.json
  23. 5
      src/translations/locales/tr.json
  24. 5
      src/translations/locales/ul.json
  25. 5
      src/translations/locales/ur.json
  26. 5
      src/translations/locales/uz.json
  27. 5
      src/translations/locales/zh.json

423
src/app/marriage-advisors/page.tsx

@ -9,11 +9,21 @@ import NavigationButton from "@/components/Componentes/navigation-button";
import { PageBackground } from "@/components/Componentes/page-background";
import SupportSheet from "@/components/Componentes/support-sheet";
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() {
const { dictionary: t } = useI18n();
const [isSupportOpen, setIsSupportOpen] = useState(false);
const { data, isLoading, isError, refetch } = useMarriageAdvisorsQuery();
const advisors = data?.results ?? [];
const mainStyle = {
backgroundImage: `url("/assets/images/islamic_pattern_2_2892_3864.svg")`,
backgroundColor: "#F5F5F5",
@ -21,62 +31,12 @@ export default function MarriageAdvisorsPage() {
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 (
<>
@ -103,124 +63,49 @@ export default function MarriageAdvisorsPage() {
</StickyHeader>
<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>
)}
{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>
</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>
))}
</>
);
}

1
src/hooks/marriage/query-keys.ts

@ -10,6 +10,7 @@ export const marriageQueryKeys = {
"contact-info",
] as const,
profile: () => [...marriageQueryKeys.all, "profile"] as const,
advisors: () => [...marriageQueryKeys.all, "advisors"] as const,
formOverview: (formId: string, locale: string) =>
[...marriageQueryKeys.all, "form-overview", formId, locale] as const,
formSection: (formId: string, slug: string, locale: string) =>

58
src/hooks/marriage/use-marriage-advisors.ts

@ -0,0 +1,58 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { http } from "@/lib/http";
import { marriageQueryKeys } from "./query-keys";
import type { QueryOptions } from "./options";
/**
* Consultant object served by GET /api/talk/marriage-consultants/ the exact
* shape the Flutter Talk app's Consultant.fromJson consumes, so the same
* object can be handed back when opening the native consultant page.
*/
export interface MarriageConsultant {
username: string;
fullname: string | null;
avatar_url: string | null;
slogan: string | null;
bio: string | null;
description: string | null;
topics: string[];
languages: string[];
call_languages: string[];
contact_type: string[];
scheduling: Record<string, unknown> | null;
status: string;
unread_count: number;
avg_rate: number;
categories: string[];
session_duration: number | null;
video_call_cost: number | null;
voice_call_cost: number | null;
first_message: string | null;
is_ai: boolean;
is_supporter: boolean;
order: number;
}
export interface MarriageConsultantsResponse {
results: MarriageConsultant[];
}
export async function getMarriageConsultants() {
const { data } = await http.get<MarriageConsultantsResponse>(
"/api/talk/marriage-consultants/",
);
return data;
}
export function useMarriageAdvisorsQuery(
options?: QueryOptions<MarriageConsultantsResponse>,
) {
return useQuery({
...options,
queryKey: marriageQueryKeys.advisors(),
queryFn: getMarriageConsultants,
});
}

5
src/translations/locales/ar.json

@ -758,5 +758,8 @@
"Select date": "اختر التاريخ",
"Day": "اليوم",
"Month": "الشهر",
"Year": "السنة"
"Year": "السنة",
"Failed to load advisors.": "تعذّر تحميل المستشارين.",
"Try again": "حاول مرة أخرى",
"No advisors are currently available.": "لا يوجد مستشارون متاحون حاليًا."
}

5
src/translations/locales/az.json

@ -758,5 +758,8 @@
"Select date": "Tarix seçin",
"Day": "Gün",
"Month": "Ay",
"Year": "İl"
"Year": "İl",
"Failed to load advisors.": "Məsləhətçiləri yükləmək alınmadı.",
"Try again": "Yenidən cəhd edin",
"No advisors are currently available.": "Hazırda mövcud məsləhətçi yoxdur."
}

5
src/translations/locales/bn.json

@ -758,5 +758,8 @@
"Select date": "তারিখ নির্বাচন করুন",
"Day": "দিন",
"Month": "মাস",
"Year": "বছর"
"Year": "বছর",
"Failed to load advisors.": "উপদেষ্টাদের লোড করা যায়নি।",
"Try again": "আবার চেষ্টা করুন",
"No advisors are currently available.": "বর্তমানে কোনো উপদেষ্টা নেই।"
}

5
src/translations/locales/da.json

@ -758,5 +758,8 @@
"Select date": "Vælg dato",
"Day": "Dag",
"Month": "Måned",
"Year": "År"
"Year": "År",
"Failed to load advisors.": "Kunne ikke indlæse rådgivere.",
"Try again": "Prøv igen",
"No advisors are currently available.": "Der er i øjeblikket ingen tilgængelige rådgivere."
}

5
src/translations/locales/de.json

@ -758,5 +758,8 @@
"Select date": "Datum auswählen",
"Day": "Tag",
"Month": "Monat",
"Year": "Jahr"
"Year": "Jahr",
"Failed to load advisors.": "Berater konnten nicht geladen werden.",
"Try again": "Erneut versuchen",
"No advisors are currently available.": "Derzeit sind keine Berater verfügbar."
}

5
src/translations/locales/en.json

@ -808,5 +808,8 @@
"Select date": "Select date",
"Day": "Day",
"Month": "Month",
"Year": "Year"
"Year": "Year",
"Failed to load advisors.": "Failed to load advisors.",
"Try again": "Try again",
"No advisors are currently available.": "No advisors are currently available."
}

5
src/translations/locales/es.json

@ -758,5 +758,8 @@
"Select date": "Seleccionar fecha",
"Day": "Día",
"Month": "Mes",
"Year": "Año"
"Year": "Año",
"Failed to load advisors.": "No se pudieron cargar los asesores.",
"Try again": "Intentar de nuevo",
"No advisors are currently available.": "Actualmente no hay asesores disponibles."
}

5
src/translations/locales/fa.json

@ -808,5 +808,8 @@
"Select date": "انتخاب تاریخ",
"Day": "روز",
"Month": "ماه",
"Year": "سال"
"Year": "سال",
"Failed to load advisors.": "بارگیری مشاوران ناموفق بود.",
"Try again": "تلاش مجدد",
"No advisors are currently available.": "در حال حاضر مشاوری در دسترس نیست."
}

5
src/translations/locales/fr.json

@ -758,5 +758,8 @@
"Select date": "Sélectionner la date",
"Day": "Jour",
"Month": "Mois",
"Year": "Année"
"Year": "Année",
"Failed to load advisors.": "Échec du chargement des conseillers.",
"Try again": "Réessayer",
"No advisors are currently available.": "Aucun conseiller n'est disponible actuellement."
}

5
src/translations/locales/gu.json

@ -758,5 +758,8 @@
"Select date": "તારીખ પસંદ કરો",
"Day": "દિવસ",
"Month": "મહિનો",
"Year": "વર્ષ"
"Year": "વર્ષ",
"Failed to load advisors.": "સલાહકારોને લોડ કરવામાં નિષ્ફળ.",
"Try again": "ફરી પ્રયાસ કરો",
"No advisors are currently available.": "હાલમાં કોઈ સલાહકાર ઉપલબ્ધ નથી."
}

5
src/translations/locales/ha.json

@ -758,5 +758,8 @@
"Select date": "Zaɓi kwanan wata",
"Day": "Rana",
"Month": "Wata",
"Year": "Shekara"
"Year": "Shekara",
"Failed to load advisors.": "Gaza loda masu ba da shawara.",
"Try again": "Sake gwadawa",
"No advisors are currently available.": "Babu masu ba da shawara a halin yanzu."
}

5
src/translations/locales/he.json

@ -286,5 +286,8 @@
"Month": "חודש",
"Year": "שנה",
"Age": "גיל",
"Confirm": "אישור"
"Confirm": "אישור",
"Failed to load advisors.": "טעינת היועצים נכשלה.",
"Try again": "נסה שוב",
"No advisors are currently available.": "אין כרגע יועצים זמינים."
}

5
src/translations/locales/hi.json

@ -758,5 +758,8 @@
"Select date": "तारीख़ चुनें",
"Day": "दिन",
"Month": "महीना",
"Year": "साल"
"Year": "साल",
"Failed to load advisors.": "सलाहकारों को लोड करने में विफल।",
"Try again": "फिर से प्रयास करें",
"No advisors are currently available.": "इस समय कोई सलाहकार उपलब्ध नहीं है।"
}

5
src/translations/locales/id.json

@ -286,5 +286,8 @@
"Month": "Bulan",
"Year": "Tahun",
"Age": "Usia",
"Confirm": "Konfirmasi"
"Confirm": "Konfirmasi",
"Failed to load advisors.": "Gagal memuat penasihat.",
"Try again": "Coba lagi",
"No advisors are currently available.": "Saat ini tidak ada penasihat yang tersedia."
}

5
src/translations/locales/ks.json

@ -286,5 +286,8 @@
"Month": "ماہ",
"Year": "ؤری",
"Age": "عمر",
"Confirm": "تصدیق کریں"
"Confirm": "تصدیق کریں",
"Failed to load advisors.": "مشیرن لوڈ کرنأک ناکام۔",
"Try again": "دوبآ کوشش کریو",
"No advisors are currently available.": "فی الحأل کاہن مشیر دستیاب چھُنہِ۔"
}

5
src/translations/locales/pt.json

@ -286,5 +286,8 @@
"Month": "Mês",
"Year": "Ano",
"Age": "Idade",
"Confirm": "Confirmar"
"Confirm": "Confirmar",
"Failed to load advisors.": "Falha ao carregar os consultores.",
"Try again": "Tentar novamente",
"No advisors are currently available.": "Nenhum consultor disponível no momento."
}

5
src/translations/locales/ru.json

@ -763,5 +763,8 @@
"Select date": "Выберите дату",
"Day": "День",
"Month": "Месяц",
"Year": "Год"
"Year": "Год",
"Failed to load advisors.": "Не удалось загрузить консультантов.",
"Try again": "Попробовать снова",
"No advisors are currently available.": "На данный момент нет доступных консультантов."
}

5
src/translations/locales/sw.json

@ -286,5 +286,8 @@
"Month": "Mwezi",
"Year": "Mwaka",
"Age": "Umri",
"Confirm": "Thibitisha"
"Confirm": "Thibitisha",
"Failed to load advisors.": "Imeshindwa kupakia washauri.",
"Try again": "Jaribu tena",
"No advisors are currently available.": "Hakuna washauri wanaopatikana kwa sasa."
}

5
src/translations/locales/tg.json

@ -286,5 +286,8 @@
"Month": "Моҳ",
"Year": "Сол",
"Age": "Синну сол",
"Confirm": "Тасдиқ кунед"
"Confirm": "Тасдиқ кунед",
"Failed to load advisors.": "Бор кардани маслиҳатдиҳондагон ноком буд.",
"Try again": "Дубора кӯшиш кунед",
"No advisors are currently available.": "Дар айни замон маслиҳатдиҳон дастрас нест."
}

5
src/translations/locales/tr.json

@ -286,5 +286,8 @@
"Month": "Ay",
"Year": "Yıl",
"Age": "Yaş",
"Confirm": "Onayla"
"Confirm": "Onayla",
"Failed to load advisors.": "Danışmanlar yüklenemedi.",
"Try again": "Tekrar dene",
"No advisors are currently available.": "Şu anda uygun danışman yok."
}

5
src/translations/locales/ul.json

@ -286,5 +286,8 @@
"Month": "ئاي",
"Year": "يىل",
"Age": "يېشى",
"Confirm": "جەزملەش"
"Confirm": "جەزملەش",
"Failed to load advisors.": "مشاورین لوڈ نہیں ہو سکے۔",
"Try again": "دوبارہ کوشش کریں",
"No advisors are currently available.": "اس وقت کوئی مشاور دستیاب نہیں ہے۔"
}

5
src/translations/locales/ur.json

@ -286,5 +286,8 @@
"Month": "مہینہ",
"Year": "سال",
"Age": "عمر",
"Confirm": "تصدیق کریں"
"Confirm": "تصدیق کریں",
"Failed to load advisors.": "مشاورین لوڈ نہیں ہو سکے۔",
"Try again": "دوبارہ کوشش کریں",
"No advisors are currently available.": "اس وقت کوئی مشاور دستیاب نہیں ہے۔"
}

5
src/translations/locales/uz.json

@ -286,5 +286,8 @@
"Month": "Oy",
"Year": "Yil",
"Age": "Yosh",
"Confirm": "Tasdiqlash"
"Confirm": "Tasdiqlash",
"Failed to load advisors.": "Maslahatchilarni yuklab bo‘lmadi.",
"Try again": "Qayta urinib ko‘ring",
"No advisors are currently available.": "Hozircha mavjud maslahatchi yo‘q."
}

5
src/translations/locales/zh.json

@ -758,5 +758,8 @@
"Select date": "选择日期",
"Day": "日",
"Month": "月",
"Year": "年"
"Year": "年",
"Failed to load advisors.": "加载顾问失败。",
"Try again": "重试",
"No advisors are currently available.": "目前没有可用的顾问。"
}
Loading…
Cancel
Save