'use client' import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react' export type AdminLanguage = 'en' | 'fr' | 'ar' export type AdminTheme = 'light' | 'dark' type AdminDictionary = { nav: Record logout: string language: string theme: string light: string dark: string overview: string admin: string platform: string loading: string } const dictionaries: Record = { en: { nav: { overview: 'Overview', companies: 'Companies', siteConfig: 'Site Config', renters: 'Renters', auditLogs: 'Audit Logs', adminUsers: 'Admin Users', billing: 'Billing', pricing: 'Pricing', notifications: 'Notifications', menuManagement: 'Menu Management', }, logout: 'Logout', language: 'Language', theme: 'Theme', light: 'Light', dark: 'Dark', overview: 'Platform overview', admin: 'Admin', platform: 'Platform', loading: 'Loading', }, fr: { nav: { overview: "Vue d'ensemble", companies: 'Entreprises', siteConfig: 'Config site', renters: 'Locataires', auditLogs: "Journaux d'audit", adminUsers: 'Utilisateurs admin', billing: 'Facturation', pricing: 'Tarification', notifications: 'Notifications', menuManagement: 'Gestion du menu', }, logout: 'Déconnexion', language: 'Langue', theme: 'Mode', light: 'Clair', dark: 'Sombre', overview: 'Vue plateforme', admin: 'Admin', platform: 'Plateforme', loading: 'Chargement', }, ar: { nav: { overview: 'نظرة عامة', companies: 'الشركات', siteConfig: 'إعدادات الموقع', renters: 'المستأجرون', auditLogs: 'سجلات التدقيق', adminUsers: 'مستخدمو الإدارة', billing: 'الفوترة', pricing: 'الأسعار', notifications: 'الإشعارات', menuManagement: 'إدارة القوائم', }, logout: 'تسجيل الخروج', language: 'اللغة', theme: 'الوضع', light: 'فاتح', dark: 'داكن', overview: 'نظرة عامة على المنصة', admin: 'الإدارة', platform: 'المنصة', loading: 'جارٍ التحميل', }, } const languageMeta = { en: { flag: '🇺🇸', shortLabel: 'EN' }, fr: { flag: '🇫🇷', shortLabel: 'FR' }, ar: { flag: '🇲🇦', shortLabel: 'AR' }, } as const const SHARED_THEME_KEY = 'rentaldrivego-theme' type AdminI18nContext = { language: AdminLanguage setLanguage: (value: AdminLanguage) => void theme: AdminTheme setTheme: (value: AdminTheme) => void dict: AdminDictionary } const Context = createContext(null) export function AdminI18nProvider({ children }: { children: React.ReactNode }) { const [language, setLanguage] = useState('en') const [theme, setTheme] = useState('light') // Skip the very first write so we don't overwrite a stored preference with // the default 'en' value before the hydration read-effect has applied it. const skipFirstLangWrite = useRef(true) useEffect(() => { const stored = window.localStorage.getItem('admin-language') const storedTheme = window.localStorage.getItem(SHARED_THEME_KEY) ?? window.localStorage.getItem('admin-theme') if (stored === 'en' || stored === 'fr' || stored === 'ar') { setLanguage(stored) } if (storedTheme === 'light' || storedTheme === 'dark') { setTheme(storedTheme) return } if (!window.matchMedia('(prefers-color-scheme: dark)').matches) { setTheme('light') } }, []) useEffect(() => { document.documentElement.lang = language document.documentElement.dir = language === 'ar' ? 'rtl' : 'ltr' if (skipFirstLangWrite.current) { skipFirstLangWrite.current = false return } window.localStorage.setItem('admin-language', language) }, [language]) useEffect(() => { document.documentElement.classList.remove('light', 'dark') document.documentElement.classList.add(theme) document.documentElement.style.colorScheme = theme document.body.dataset.theme = theme document.cookie = `${SHARED_THEME_KEY}=${encodeURIComponent(theme)}; path=/; max-age=31536000; SameSite=Lax` window.localStorage.setItem(SHARED_THEME_KEY, theme) window.localStorage.setItem('admin-theme', theme) }, [theme]) const value = useMemo( () => ({ language, setLanguage, theme, setTheme, dict: dictionaries[language] }), [language, theme], ) return {children} } export function useAdminI18n() { const context = useContext(Context) if (!context) throw new Error('useAdminI18n must be used within AdminI18nProvider') return context } export function AdminLanguageSwitcher() { const { language, setLanguage } = useAdminI18n() const [open, setOpen] = useState(false) const [embedded, setEmbedded] = useState(false) const ref = useRef(null) useEffect(() => { setEmbedded(window.self !== window.top) }, []) useEffect(() => { if (!open) return function onMouseDown(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) } document.addEventListener('mousedown', onMouseDown) return () => document.removeEventListener('mousedown', onMouseDown) }, [open]) if (embedded) return null const current = languageMeta[language] return (
{open && (
{(['en', 'fr', 'ar'] as AdminLanguage[]).map((value) => { const meta = languageMeta[value] const active = value === language return ( ) })}
)}
) } export function AdminThemeSwitcher() { const { theme, setTheme, dict } = useAdminI18n() const [open, setOpen] = useState(false) const [embedded, setEmbedded] = useState(false) const ref = useRef(null) useEffect(() => { setEmbedded(window.self !== window.top) }, []) useEffect(() => { if (!open) return function onMouseDown(e: MouseEvent) { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) } document.addEventListener('mousedown', onMouseDown) return () => document.removeEventListener('mousedown', onMouseDown) }, [open]) if (embedded) return null return (
{open && (
{(['light', 'dark'] as AdminTheme[]).map((value) => { const active = value === theme return ( ) })}
)}
) }