Add FA/EN locale, home activity charts, and theme-aware polish.

Ship shared LocaleProvider, business/customer i18n, branding defaultLocale, curated home tiles with dual 30-day charts, and chart/page aura tokens; refresh PROJECT_CONTEXT.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-01 09:35:27 +03:30
co-authored by Cursor
parent f5b2193ba1
commit 66004a0fba
112 changed files with 4338 additions and 1061 deletions
@@ -4,10 +4,12 @@ import {
useContext,
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
} from 'react'
import { applyDocumentFavicon } from '@meshkee/dashboard-core'
import { useLocale } from '@meshkee/dashboard-ui'
import { isAbortError } from '../lib/api'
import { getBusinessDomain } from '../lib/config'
import { BUSINESS_PROFILE_UPDATED_EVENT } from '../lib/businessContext'
@@ -34,10 +36,13 @@ function pickBusinessName(
}
export function TenantBrandingProvider({ children }: { children: ReactNode }) {
const [businessName, setBusinessName] = useState('')
const { locale, setLocale } = useLocale()
const [nameEn, setNameEn] = useState('')
const [nameFa, setNameFa] = useState('')
const [logoUrl, setLogoUrl] = useState<string | null>(null)
const [faviconUrl, setFaviconUrl] = useState<string | null>(null)
const [refreshToken, setRefreshToken] = useState(0)
const defaultLocaleAppliedRef = useRef(false)
const refreshBranding = useCallback(() => {
setRefreshToken((value) => value + 1)
@@ -52,7 +57,8 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
const tenant = await resolveTenantByDomain(domain)
if (controller.signal.aborted) return
let name = pickBusinessName(tenant.name, tenant.nameFa, domain)
let nextNameEn = pickBusinessName(tenant.name, domain)
let nextNameFa = pickBusinessName(tenant.nameFa, tenant.name, domain)
let nextLogo = tenant.logoUrl?.trim() || null
let nextFavicon =
tenant.faviconUrl?.trim() || tenant.logoUrl?.trim() || null
@@ -60,10 +66,15 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
try {
const profile = await getBusinessProfile(controller.signal)
if (!controller.signal.aborted) {
name = pickBusinessName(
nextNameEn = pickBusinessName(
profile.profile.nameEn,
nextNameEn,
domain,
)
nextNameFa = pickBusinessName(
profile.profile.nameFa,
name,
profile.profile.nameEn,
nextNameFa,
domain,
)
nextLogo = profile.profile.logoUrl?.trim() || nextLogo
@@ -79,14 +90,25 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
}
if (!controller.signal.aborted) {
setBusinessName(name || domain)
setNameEn(nextNameEn || domain)
setNameFa(nextNameFa || nextNameEn || domain)
setLogoUrl(nextLogo)
setFaviconUrl(nextFavicon)
applyDocumentFavicon(nextFavicon)
if (!defaultLocaleAppliedRef.current) {
defaultLocaleAppliedRef.current = true
if (tenant.defaultLocale === 'en' || tenant.defaultLocale === 'fa') {
setLocale(tenant.defaultLocale)
} else {
setLocale('fa')
}
}
}
} catch (err) {
if (isAbortError(err) || controller.signal.aborted) return
setBusinessName(domain)
const domain = getBusinessDomain()
setNameEn(domain)
setNameFa(domain)
setLogoUrl(null)
setFaviconUrl(null)
applyDocumentFavicon(null)
@@ -104,7 +126,14 @@ export function TenantBrandingProvider({ children }: { children: ReactNode }) {
controller.abort()
window.removeEventListener(BUSINESS_PROFILE_UPDATED_EVENT, handleProfileUpdated)
}
}, [refreshToken, refreshBranding])
}, [refreshToken, refreshBranding, setLocale])
const businessName = useMemo(() => {
if (locale === 'fa') {
return nameFa.trim() || nameEn.trim() || getBusinessDomain()
}
return nameEn.trim() || nameFa.trim() || getBusinessDomain()
}, [locale, nameEn, nameFa])
const value = useMemo(
() => ({ businessName, logoUrl, faviconUrl, refreshBranding }),