fix setting and add rental policies
Test / Type Check (all packages) (push) Has been cancelled
Test / API Unit Tests (push) Has been cancelled
Test / Homepage Unit Tests (push) Has been cancelled
Test / Carplace Unit Tests (push) Has been cancelled
Test / Admin Unit Tests (push) Has been cancelled
Test / Dashboard Unit Tests (push) Has been cancelled
Test / API Integration Tests (push) Has been cancelled
Build & Push / Pipeline Tests (push) Waiting to run
Build & Push / Build & Push Docker Image (push) Blocked by required conditions

This commit is contained in:
root
2026-07-20 22:47:52 -04:00
parent d13ae2205f
commit 8f5ca1df22
24 changed files with 584 additions and 106 deletions
@@ -294,6 +294,7 @@ type VehicleDetailDict = {
saveLabel: string
savingLabel: string
dateRequired: string
carMaintenance: string
routineTypes: Record<string, string>
}
@@ -532,11 +533,13 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
saveLabel: 'Save',
savingLabel: 'Saving…',
dateRequired: 'Service date is required.',
carMaintenance: 'Car Maintenance',
routineTypes: {
'Oil Change': 'Oil Change',
'Technical Inspection': 'Technical Inspection',
'Vehicle Registration': 'Vehicle Registration',
'Car Tax': 'Car Tax',
'Insurance': 'Insurance',
'Tire Rotation': 'Tire Rotation',
'Brake Inspection': 'Brake Inspection',
'Air Filter': 'Air Filter',
@@ -545,6 +548,15 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'Belt / Chain Service': 'Belt / Chain Service',
'Coolant Flush': 'Coolant Flush',
'Transmission Service': 'Transmission Service',
'Wheel Alignment': 'Wheel Alignment',
'Wheel Balancing': 'Wheel Balancing',
'Spark Plugs': 'Spark Plugs',
'Fuel Filter': 'Fuel Filter',
'Wiper Blades': 'Wiper Blades',
'AC Service': 'AC Service',
'Suspension Check': 'Suspension Check',
'Engine Diagnostic': 'Engine Diagnostic',
'Brake Pads': 'Brake Pads',
},
},
calendar: {
@@ -890,11 +902,13 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
saveLabel: 'Enregistrer',
savingLabel: 'Enregistrement…',
dateRequired: 'La date du service est requise.',
carMaintenance: 'Entretien voiture',
routineTypes: {
'Oil Change': "Vidange d'huile",
'Technical Inspection': 'Contrôle technique',
'Vehicle Registration': 'Carte grise',
'Car Tax': 'Vignette automobile',
'Insurance': 'Assurance',
'Tire Rotation': 'Rotation des pneus',
'Brake Inspection': 'Contrôle des freins',
'Air Filter': 'Filtre à air',
@@ -903,6 +917,15 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'Belt / Chain Service': 'Courroie / Chaîne',
'Coolant Flush': 'Liquide de refroidissement',
'Transmission Service': 'Entretien transmission',
'Wheel Alignment': 'Parallélisme',
'Wheel Balancing': 'Équilibrage des roues',
'Spark Plugs': 'Bougies',
'Fuel Filter': 'Filtre à carburant',
'Wiper Blades': "Balais d'essuie-glace",
'AC Service': 'Climatisation',
'Suspension Check': 'Contrôle suspension',
'Engine Diagnostic': 'Diagnostic moteur',
'Brake Pads': 'Plaquettes de frein',
},
},
calendar: {
@@ -1248,11 +1271,13 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
saveLabel: 'حفظ',
savingLabel: 'جارٍ الحفظ…',
dateRequired: 'تاريخ الخدمة مطلوب.',
carMaintenance: 'صيانة السيارة',
routineTypes: {
'Oil Change': 'تغيير الزيت',
'Technical Inspection': 'الفحص التقني',
'Vehicle Registration': 'تسجيل المركبة',
'Car Tax': 'الضريبة السنوية',
'Insurance': 'التأمين',
'Tire Rotation': 'تدوير الإطارات',
'Brake Inspection': 'فحص الفرامل',
'Air Filter': 'فلتر الهواء',
@@ -1261,6 +1286,15 @@ const dictionaries: Record<DashboardLanguage, DashboardDictionary> = {
'Belt / Chain Service': 'خدمة السير / السلسلة',
'Coolant Flush': 'تغيير سائل التبريد',
'Transmission Service': 'خدمة ناقل الحركة',
'Wheel Alignment': 'ضبط زوايا العجلات',
'Wheel Balancing': 'ترصيص العجلات',
'Spark Plugs': 'شمعات الإشعال',
'Fuel Filter': 'فلتر الوقود',
'Wiper Blades': 'مساحات الزجاج',
'AC Service': 'صيانة المكيف',
'Suspension Check': 'فحص نظام التعليق',
'Engine Diagnostic': 'تشخيص المحرك',
'Brake Pads': 'صفائح الفرامل',
},
},
calendar: {
@@ -2,7 +2,7 @@
import Link from 'next/link'
import { Bell, Search, Settings } from 'lucide-react'
import { usePathname, useRouter } from 'next/navigation'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useState, useEffect } from 'react'
import { io } from 'socket.io-client'
import { EMPLOYEE_PROFILE_KEY, apiFetch } from '@/lib/api'
@@ -33,11 +33,20 @@ function resolveSocketOrigin(): string | null {
return window.location.origin
}
const SEARCHABLE_ROUTES = ['/reservations', '/fleet', '/customers', '/contracts', '/billing']
function getSearchTarget(appPath: string): string {
const match = SEARCHABLE_ROUTES.find((route) => appPath === route || appPath.startsWith(`${route}/`))
return match ?? '/reservations'
}
export default function TopBar() {
const { dict } = useDashboardI18n()
const pathname = usePathname()
const appPath = toDashboardAppPath(pathname)
const router = useRouter()
const searchParams = useSearchParams()
const [globalSearch, setGlobalSearch] = useState('')
const [unreadCount, setUnreadCount] = useState(0)
const [showNotifs, setShowNotifs] = useState(false)
const [userInitials, setUserInitials] = useState('W')
@@ -54,6 +63,10 @@ export default function TopBar() {
const [mounted, setMounted] = useState(false)
useEffect(() => { setMounted(true) }, [])
useEffect(() => {
setGlobalSearch(searchParams.get('search') ?? '')
}, [searchParams])
const title = (() => {
if (!mounted) return dict.titles['/']
if (dict.titles[appPath]) return dict.titles[appPath]
@@ -193,18 +206,35 @@ export default function TopBar() {
router.push('/notifications')
}
function submitSearch(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
const query = globalSearch.trim()
const target = getSearchTarget(appPath)
const params = new URLSearchParams()
if (query) {
params.set('search', query)
router.push(`${target}?${params.toString()}`)
} else {
router.push(target)
}
}
return (
<header className="relative z-[70] flex h-16 items-center justify-between border-b border-blue-200/70 bg-white/82 px-6 text-blue-950 backdrop-blur-xl transition-colors dark:border-blue-400/10 dark:bg-[#0a0f1a]/90 dark:text-slate-100">
<div className="flex items-center gap-4 min-w-0">
<h1 className="shrink-0 text-lg font-semibold text-blue-950 dark:text-slate-50">{title}</h1>
<div className="relative hidden w-[min(380px,38vw)] sm:block">
<form className="relative hidden w-[min(380px,38vw)] sm:block" onSubmit={submitSearch}>
<Search className="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-500" />
<input
type="search"
placeholder="Search vehicles, bookings, customers..."
value={globalSearch}
onChange={(event) => setGlobalSearch(event.target.value)}
className="h-10 w-full rounded-lg border border-blue-200/80 bg-blue-50/70 pl-10 pr-4 text-sm text-blue-950 outline-none transition-all placeholder:text-slate-500 focus:border-blue-400/45 focus:bg-white focus:ring-4 focus:ring-blue-500/10 dark:border-blue-400/15 dark:bg-blue-500/[0.06] dark:text-slate-100 dark:focus:bg-blue-500/10"
/>
</div>
</form>
<div className="hidden items-center gap-2 text-sm text-slate-400 xl:flex">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400 shadow-[0_0_0_5px_rgba(16,185,129,0.10)]" />
System operational