fix first online resevation

This commit is contained in:
root
2026-05-09 20:01:51 -04:00
parent c4a45c8b21
commit 09b0e3b55f
75 changed files with 6394 additions and 2190 deletions
+85 -11
View File
@@ -3,6 +3,7 @@
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
export type DashboardLanguage = 'en' | 'fr' | 'ar'
export type DashboardTheme = 'light' | 'dark'
type DashboardDictionary = {
nav: Record<string, string>
@@ -11,9 +12,10 @@ type DashboardDictionary = {
noNewNotifications: string
unreadNotifications: (count: number) => string
signOut: string
demoUser: string
clerkDisabled: string
workspaceUser: string
localAuth: string
language: string
theme: string
light: string
dark: string
}
@@ -24,6 +26,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'Dashboard',
fleet: 'Fleet',
reservations: 'Reservations',
onlineReservations: 'Online Reservations',
customers: 'Customers',
offers: 'Offers',
team: 'Team',
@@ -36,6 +39,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'Dashboard',
'/dashboard/fleet': 'Fleet Management',
'/dashboard/reservations': 'Reservations',
'/dashboard/online-reservations': 'Online Reservations',
'/dashboard/customers': 'Customers',
'/dashboard/offers': 'Offers',
'/dashboard/team': 'Team',
@@ -47,9 +51,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'No new notifications',
unreadNotifications: (count) => `${count} unread notification${count > 1 ? 's' : ''}`,
signOut: 'Sign out',
demoUser: 'Demo User',
clerkDisabled: 'Clerk disabled in local dev',
workspaceUser: 'Workspace user',
localAuth: 'Local authentication',
language: 'Language',
theme: 'Theme',
light: 'Light',
dark: 'Dark',
},
@@ -58,6 +63,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'Tableau de bord',
fleet: 'Flotte',
reservations: 'Réservations',
onlineReservations: 'Réservations en ligne',
customers: 'Clients',
offers: 'Offres',
team: 'Équipe',
@@ -70,6 +76,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'Tableau de bord',
'/dashboard/fleet': 'Gestion de flotte',
'/dashboard/reservations': 'Réservations',
'/dashboard/online-reservations': 'Réservations en ligne',
'/dashboard/customers': 'Clients',
'/dashboard/offers': 'Offres',
'/dashboard/team': 'Équipe',
@@ -81,9 +88,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'Aucune nouvelle notification',
unreadNotifications: (count) => `${count} notification${count > 1 ? 's' : ''} non lue${count > 1 ? 's' : ''}`,
signOut: 'Déconnexion',
demoUser: 'Utilisateur démo',
clerkDisabled: 'Clerk désactivé en local',
workspaceUser: 'Utilisateur espace',
localAuth: 'Authentification locale',
language: 'Langue',
theme: 'Mode',
light: 'Clair',
dark: 'Sombre',
},
@@ -92,6 +100,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
dashboard: 'لوحة التحكم',
fleet: 'الأسطول',
reservations: 'الحجوزات',
onlineReservations: 'الحجوزات الإلكترونية',
customers: 'العملاء',
offers: 'العروض',
team: 'الفريق',
@@ -104,6 +113,7 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'/dashboard': 'لوحة التحكم',
'/dashboard/fleet': 'إدارة الأسطول',
'/dashboard/reservations': 'الحجوزات',
'/dashboard/online-reservations': 'الحجوزات الإلكترونية',
'/dashboard/customers': 'العملاء',
'/dashboard/offers': 'العروض',
'/dashboard/team': 'الفريق',
@@ -115,9 +125,10 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
noNewNotifications: 'لا توجد إشعارات جديدة',
unreadNotifications: (count) => `${count} إشعار غير مقروء`,
signOut: 'تسجيل الخروج',
demoUser: 'مستخدم تجريبي',
clerkDisabled: 'Clerk غير مفعّل محلياً',
workspaceUser: 'مستخدم مساحة العمل',
localAuth: 'مصادقة محلية',
language: 'اللغة',
theme: 'الوضع',
light: 'فاتح',
dark: 'داكن',
},
@@ -126,6 +137,8 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
type I18nContextValue = {
language: DashboardLanguage
setLanguage: (value: DashboardLanguage) => void
theme: DashboardTheme
setTheme: (value: DashboardTheme) => void
dict: DashboardDictionary
}
@@ -133,12 +146,24 @@ const I18nContext = createContext<I18nContextValue | null>(null)
export function DashboardI18nProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguage] = useState<DashboardLanguage>('en')
const [theme, setTheme] = useState<DashboardTheme>('light')
useEffect(() => {
const stored = window.localStorage.getItem('dashboard-language')
const storedTheme = window.localStorage.getItem('dashboard-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('dark')
}
}, [])
useEffect(() => {
@@ -147,7 +172,18 @@ export function DashboardI18nProvider({ children }: { children: React.ReactNode
window.localStorage.setItem('dashboard-language', language)
}, [language])
const value = useMemo(() => ({ language, setLanguage, dict: dictionaries[language] }), [language])
useEffect(() => {
document.documentElement.classList.remove('light', 'dark')
document.documentElement.classList.add(theme)
document.documentElement.style.colorScheme = theme
document.body.dataset.theme = theme
window.localStorage.setItem('dashboard-theme', theme)
}, [theme])
const value = useMemo(
() => ({ language, setLanguage, theme, setTheme, dict: dictionaries[language] }),
[language, theme],
)
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
}
@@ -168,7 +204,7 @@ export function DashboardLanguageSwitcher() {
if (embedded) return null
return (
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm">
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm transition-colors dark:border-slate-700 dark:bg-slate-900">
<span className="px-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500">
{dict.language}
</span>
@@ -180,7 +216,9 @@ export function DashboardLanguageSwitcher() {
type="button"
onClick={() => setLanguage(value)}
className={`rounded-full px-3 py-1.5 text-xs font-semibold transition ${
active ? 'bg-slate-900 text-white' : 'text-slate-600 hover:bg-slate-100'
active
? 'bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-950'
: 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'
}`}
>
{value.toUpperCase()}
@@ -190,3 +228,39 @@ export function DashboardLanguageSwitcher() {
</div>
)
}
export function DashboardThemeSwitcher() {
const { theme, setTheme, dict } = useDashboardI18n()
const [embedded, setEmbedded] = useState(false)
useEffect(() => {
setEmbedded(window.self !== window.top)
}, [])
if (embedded) return null
return (
<div className="flex items-center gap-1 rounded-full border border-slate-200 bg-white px-2 py-1 shadow-sm transition-colors dark:border-slate-700 dark:bg-slate-900">
<span className="px-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400">
{dict.theme}
</span>
{(['light', 'dark'] as DashboardTheme[]).map((value) => {
const active = value === theme
return (
<button
key={value}
type="button"
onClick={() => setTheme(value)}
className={`rounded-full px-3 py-1.5 text-xs font-semibold transition ${
active
? 'bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-950'
: 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'
}`}
>
{value === 'light' ? dict.light : dict.dark}
</button>
)
})}
</div>
)
}