Browse Source

feat(support): open teamby support portal with active theme and lang

master
nwhco 4 days ago
parent
commit
35c724c986
  1. 15
      src/components/layout/AppSidebar.tsx
  2. 3
      src/config/navigation.ts
  3. 67
      src/lib/support.ts

15
src/components/layout/AppSidebar.tsx

@ -1,3 +1,4 @@
import { openSupportTicketPortal, prefetchSupportUrl } from '@/lib/support'
import { NavLink, useLocation } from 'react-router-dom' import { NavLink, useLocation } from 'react-router-dom'
import { Ic, type IconName } from '@/icons' import { Ic, type IconName } from '@/icons'
import { import {
@ -28,6 +29,7 @@ function BillingSidebarBadges() {
const { t } = useTranslation() const { t } = useTranslation()
React.useEffect(() => { React.useEffect(() => {
prefetchSupportUrl()
fetchCounts() fetchCounts()
// Poll every 30 seconds for live updates // Poll every 30 seconds for live updates
const interval = setInterval(fetchCounts, 30000) const interval = setInterval(fetchCounts, 30000)
@ -268,6 +270,19 @@ function NavItem({ item }: { item: NavItemConfig }) {
)} )}
</> </>
) )
if (item.isExternalSupport) {
return (
<SidebarMenuItem>
<SidebarMenuButton
tooltip={item.title}
onClick={() => openSupportTicketPortal()}
className="cursor-pointer text-primary hover:bg-primary/10"
>
{inner}
</SidebarMenuButton>
</SidebarMenuItem>
)
}
return ( return (
<SidebarMenuItem> <SidebarMenuItem>

3
src/config/navigation.ts

@ -12,6 +12,7 @@ export interface NavSubItemConfig {
} }
export interface NavItemConfig { export interface NavItemConfig {
isExternalSupport?: boolean
title: string title: string
titleKey?: string titleKey?: string
to?: 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.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.faq'), titleKey: 'navigation.faq', to: '/services/faq', icon: 'helpCircle' },
{ title: t('navigation.settings'), titleKey: 'navigation.settings', to: '/settings', icon: 'settings' }, { title: t('navigation.settings'), titleKey: 'navigation.settings', to: '/settings', icon: 'settings' },
], ],

67
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<void> {
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')
}
}
Loading…
Cancel
Save