diff --git a/src/components/layout/AppSidebar.tsx b/src/components/layout/AppSidebar.tsx index 7e5f5c4..bf50fcf 100644 --- a/src/components/layout/AppSidebar.tsx +++ b/src/components/layout/AppSidebar.tsx @@ -1,3 +1,4 @@ +import { openSupportTicketPortal, prefetchSupportUrl } from '@/lib/support' import { NavLink, useLocation } from 'react-router-dom' import { Ic, type IconName } from '@/icons' import { @@ -28,6 +29,7 @@ function BillingSidebarBadges() { const { t } = useTranslation() React.useEffect(() => { + prefetchSupportUrl() fetchCounts() // Poll every 30 seconds for live updates const interval = setInterval(fetchCounts, 30000) @@ -268,6 +270,19 @@ function NavItem({ item }: { item: NavItemConfig }) { )} ) + if (item.isExternalSupport) { + return ( + + openSupportTicketPortal()} + className="cursor-pointer text-primary hover:bg-primary/10" + > + {inner} + + + ) + } return ( diff --git a/src/config/navigation.ts b/src/config/navigation.ts index a77f530..042840c 100644 --- a/src/config/navigation.ts +++ b/src/config/navigation.ts @@ -12,6 +12,7 @@ export interface NavSubItemConfig { } export interface NavItemConfig { + isExternalSupport?: boolean title: string titleKey?: string to?: string @@ -127,7 +128,7 @@ export function getNavigationConfig(t: (key: string) => string): NavGroupConfig[ ], }, { title: t('navigation.costEstimate'), titleKey: 'navigation.costEstimate', to: '/services/cost-estimate', icon: 'calculator' }, - { title: t('navigation.support'), titleKey: 'navigation.support', to: '/services/consultant', icon: 'headset' }, + { title: t('navigation.support'), titleKey: 'navigation.support', isExternalSupport: true, icon: 'headset' }, { title: t('navigation.faq'), titleKey: 'navigation.faq', to: '/services/faq', icon: 'helpCircle' }, { title: t('navigation.settings'), titleKey: 'navigation.settings', to: '/settings', icon: 'settings' }, ], diff --git a/src/lib/support.ts b/src/lib/support.ts new file mode 100644 index 0000000..ca5b89a --- /dev/null +++ b/src/lib/support.ts @@ -0,0 +1,67 @@ +import { apiFetch } from '@/services/http' + +let cachedSupportUrl: string | null = null + +function getActiveLanguageAndTheme(): { lang: string; theme: string } { + const isDark = document.documentElement.classList.contains('dark') || document.body.classList.contains('dark') + const theme = isDark ? 'dark' : 'light' + + const htmlLang = document.documentElement.lang || '' + const htmlDir = document.documentElement.dir || '' + const storedLang = localStorage.getItem('i18nextLng') || localStorage.getItem('lang') || '' + + let lang = 'fa' + if (htmlLang.startsWith('en') || storedLang.startsWith('en')) { + lang = 'en' + } else if (htmlDir === 'ltr' && !storedLang.startsWith('fa')) { + lang = 'en' + } + + return { lang, theme } +} + +export function prefetchSupportUrl(): void { + if (cachedSupportUrl) return + const { lang, theme } = getActiveLanguageAndTheme() + apiFetch<{ success: boolean; url: string }>(`/support/ticket-url/?lang=${lang}&theme=${theme}`) + .then((data) => { + if (data && data.url) { + cachedSupportUrl = data.url + } + }) + .catch(() => { + // Ignore background errors + }) +} + +export async function openSupportTicketPortal(): Promise { + const { lang, theme } = getActiveLanguageAndTheme() + + let baseUrl = cachedSupportUrl + + if (!baseUrl) { + try { + const data = await apiFetch<{ success: boolean; url: string }>(`/support/ticket-url/?lang=${lang}&theme=${theme}`) + if (data && data.url) { + cachedSupportUrl = data.url + baseUrl = data.url + } + } catch { + // ignore + } + } + + if (!baseUrl) { + baseUrl = 'https://teamby.nwhco.ir/portal/76cb859c-24e3-4309-9d99-504ac2e49268/' + } + + try { + const urlObj = new URL(baseUrl) + urlObj.searchParams.set('lang', lang) + urlObj.searchParams.set('theme', theme) + window.open(urlObj.toString(), '_blank') + } catch { + const sep = baseUrl.includes('?') ? '&' : '?' + window.open(`${baseUrl}${sep}lang=${lang}&theme=${theme}`, '_blank') + } +}