update website home and dashboard

This commit is contained in:
root
2026-05-20 14:52:26 -04:00
parent d4f7de5762
commit fb15883f8e
38 changed files with 3043 additions and 1922 deletions
+19 -2
View File
@@ -1,6 +1,6 @@
'use client'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react'
export type AdminLanguage = 'en' | 'fr' | 'ar'
export type AdminTheme = 'light' | 'dark'
@@ -81,6 +81,12 @@ const dictionaries: Record<AdminLanguage, AdminDictionary> = {
},
}
const languageMeta = {
en: { flag: '🇺🇸', shortLabel: 'EN' },
fr: { flag: '🇫🇷', shortLabel: 'FR' },
ar: { flag: '🇲🇦', shortLabel: 'AR' },
} as const
type AdminI18nContext = {
language: AdminLanguage
setLanguage: (value: AdminLanguage) => void
@@ -94,6 +100,9 @@ const Context = createContext<AdminI18nContext | null>(null)
export function AdminI18nProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguage] = useState<AdminLanguage>('en')
const [theme, setTheme] = useState<AdminTheme>('dark')
// 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')
@@ -115,6 +124,10 @@ export function AdminI18nProvider({ children }: { children: React.ReactNode }) {
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])
@@ -154,6 +167,7 @@ export function AdminLanguageSwitcher() {
<span className="px-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-zinc-500">{dict.language}</span>
{(['en', 'fr', 'ar'] as AdminLanguage[]).map((value) => {
const active = value === language
const meta = languageMeta[value]
return (
<button
key={value}
@@ -163,7 +177,10 @@ export function AdminLanguageSwitcher() {
active ? 'bg-zinc-100 text-zinc-950' : 'text-zinc-300 hover:bg-zinc-800'
}`}
>
{value.toUpperCase()}
<span className="inline-flex items-center gap-1.5">
<span aria-hidden="true">{meta.flag}</span>
<span>{meta.shortLabel}</span>
</span>
</button>
)
})}
@@ -0,0 +1,47 @@
'use client'
import {
AdminLanguageSwitcher,
AdminThemeSwitcher,
useAdminI18n,
} from '@/components/I18nProvider'
const languageMeta = {
en: { flag: '🇺🇸', shortLabel: 'EN' },
fr: { flag: '🇫🇷', shortLabel: 'FR' },
ar: { flag: '🇲🇦', shortLabel: 'AR' },
} as const
export default function PublicFooter() {
const { language } = useAdminI18n()
const currentLanguage = languageMeta[language]
const dict = {
en: {
preferences: 'Admin preferences',
},
fr: {
preferences: 'Preferences admin',
},
ar: {
preferences: 'تفضيلات الإدارة',
},
}[language]
return (
<footer className="border-t border-stone-200 bg-white/90 px-4 py-4 backdrop-blur-md transition-colors dark:border-zinc-800 dark:bg-zinc-950/90">
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-3 lg:flex-row">
<div className="flex items-center gap-3 text-xs font-medium uppercase tracking-[0.16em] text-stone-400 dark:text-zinc-500">
<p>{dict.preferences}</p>
<span className="inline-flex items-center gap-1 rounded-full border border-zinc-700 bg-zinc-900 px-2.5 py-1 text-zinc-200">
<span aria-hidden="true">{currentLanguage.flag}</span>
<span>{currentLanguage.shortLabel}</span>
</span>
</div>
<div className="flex flex-col items-center gap-3 sm:flex-row">
<AdminLanguageSwitcher />
<AdminThemeSwitcher />
</div>
</div>
</footer>
)
}
@@ -0,0 +1,49 @@
'use client'
import Link from 'next/link'
import { useAdminI18n } from '@/components/I18nProvider'
const languageMeta = {
en: { flag: '🇺🇸', shortLabel: 'EN' },
fr: { flag: '🇫🇷', shortLabel: 'FR' },
ar: { flag: '🇲🇦', shortLabel: 'AR' },
} as const
export default function PublicHeader() {
const { language } = useAdminI18n()
const currentLanguage = languageMeta[language]
const dict = {
en: {
admin: 'Admin Console',
signIn: 'Sign in',
},
fr: {
admin: 'Console admin',
signIn: 'Connexion',
},
ar: {
admin: 'لوحة الإدارة',
signIn: 'تسجيل الدخول',
},
}[language]
return (
<header className="sticky top-0 z-30 border-b border-zinc-800 bg-zinc-950/90 backdrop-blur-md transition-colors">
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-4">
<Link href="/" className="flex items-center gap-3">
<span className="text-xs font-semibold uppercase tracking-[0.24em] text-emerald-400">RentalDriveGo</span>
<span className="hidden text-sm font-semibold text-zinc-400 sm:inline">{dict.admin}</span>
</Link>
<nav className="flex items-center gap-2">
<span className="inline-flex min-w-[3.5rem] items-center justify-center gap-1 rounded-full border border-zinc-700 bg-zinc-900 px-2.5 py-2 text-xs font-semibold text-zinc-200">
<span aria-hidden="true">{currentLanguage.flag}</span>
<span>{currentLanguage.shortLabel}</span>
</span>
<Link href="/login" className="rounded-full bg-zinc-100 px-4 py-2 text-sm font-semibold text-zinc-950 transition hover:bg-zinc-300">
{dict.signIn}
</Link>
</nav>
</div>
</header>
)
}
+6 -51
View File
@@ -1,59 +1,14 @@
'use client'
import Link from 'next/link'
import {
AdminLanguageSwitcher,
AdminThemeSwitcher,
useAdminI18n,
} from '@/components/I18nProvider'
import PublicFooter from '@/components/PublicFooter'
import PublicHeader from '@/components/PublicHeader'
export default function PublicShell({ children }: { children: React.ReactNode }) {
const { language } = useAdminI18n()
const dict = {
en: {
admin: 'Admin Console',
signIn: 'Sign in',
preferences: 'Admin preferences',
},
fr: {
admin: 'Console admin',
signIn: 'Connexion',
preferences: 'Preferences admin',
},
ar: {
admin: 'لوحة الإدارة',
signIn: 'تسجيل الدخول',
preferences: 'تفضيلات الإدارة',
},
}[language]
return (
<div className="min-h-screen bg-zinc-950 text-zinc-100 transition-colors">
<header className="sticky top-0 z-30 border-b border-zinc-800 bg-zinc-950/90 backdrop-blur-md transition-colors">
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-4">
<Link href="/" className="flex items-center gap-3">
<span className="text-xs font-semibold uppercase tracking-[0.24em] text-emerald-400">RentalDriveGo</span>
<span className="hidden text-sm font-semibold text-zinc-400 sm:inline">{dict.admin}</span>
</Link>
<nav className="flex items-center gap-2">
<Link href="/login" className="rounded-full bg-zinc-100 px-4 py-2 text-sm font-semibold text-zinc-950 transition hover:bg-zinc-300">
{dict.signIn}
</Link>
</nav>
</div>
</header>
<div>{children}</div>
<footer className="border-t border-stone-200 bg-white/90 px-4 py-4 backdrop-blur-md transition-colors dark:border-zinc-800 dark:bg-zinc-950/90">
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-3 lg:flex-row">
<p className="text-xs font-medium uppercase tracking-[0.16em] text-stone-400 dark:text-zinc-500">
{dict.preferences}
</p>
<div className="flex flex-col items-center gap-3 sm:flex-row">
<AdminLanguageSwitcher />
<AdminThemeSwitcher />
</div>
</div>
</footer>
<div className="flex min-h-screen flex-col bg-zinc-950 text-zinc-100 transition-colors">
<PublicHeader />
<div className="flex flex-1 flex-col">{children}</div>
<PublicFooter />
</div>
)
}