dashboard fix

This commit is contained in:
root
2026-05-19 21:25:38 -04:00
parent 7ac0098c7f
commit d4f7de5762
27 changed files with 705 additions and 336 deletions
@@ -140,7 +140,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
firstName: form.firstName,
lastName: form.lastName,
email: form.email,
phone: form.phone || undefined,
phone: form.phone,
startDate: new Date(`${form.startDate}T${form.startTime}`).toISOString(),
endDate: new Date(`${form.endDate}T${form.endTime}`).toISOString(),
notes: form.notes || undefined,
@@ -267,7 +267,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
{/* Dates + Times */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.pickupDate}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.pickupDate} <span className="text-red-500">*</span></label>
<input
type="date"
required
@@ -278,7 +278,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.returnDate}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.returnDate} <span className="text-red-500">*</span></label>
<input
type="date"
required
@@ -289,7 +289,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.pickupTime}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.pickupTime} <span className="text-red-500">*</span></label>
<input
type="time"
required
@@ -299,7 +299,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.dropoffTime}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.dropoffTime} <span className="text-red-500">*</span></label>
<input
type="time"
required
@@ -321,7 +321,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
{/* Customer info */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.firstName}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.firstName} <span className="text-red-500">*</span></label>
<input
type="text"
required
@@ -331,7 +331,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.lastName}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.lastName} <span className="text-red-500">*</span></label>
<input
type="text"
required
@@ -343,7 +343,7 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.email}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.email} <span className="text-red-500">*</span></label>
<input
type="email"
required
@@ -354,9 +354,10 @@ export default function ExploreVehicleGrid({ vehicles, dict }: { vehicles: Vehic
</div>
<div>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.phone}</label>
<label className="block text-xs font-medium text-stone-600 mb-1">{dict.phone} <span className="text-red-500">*</span></label>
<input
type="tel"
required
value={form.phone}
onChange={set('phone')}
className="w-full rounded-xl border border-stone-200 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-stone-900"
@@ -2,6 +2,7 @@ import Link from 'next/link'
import { marketplaceFetchOrDefault } from '@/lib/api'
import { getMarketplaceLanguage } from '@/lib/i18n.server'
import { formatCurrency } from '@rentaldrivego/types'
import BookingForm from '@/components/BookingForm'
interface VehicleDetail {
id: string
@@ -32,43 +33,37 @@ export default async function VehicleDetailPage({ params }: { params: { slug: st
const language = getMarketplaceLanguage()
const dict = {
en: {
unavailable: 'Marketplace unavailable',
unavailableTitle: 'Vehicle details are temporarily unavailable.',
unavailableBody: 'The marketplace frontend is running, but the backing database is not reachable right now.',
backToFleet: 'Back to company fleet',
noPhotos: 'No photos available.',
rentalCompany: 'Rental company',
perDay: '/ day',
continueTo: 'Continue to',
bookingSite: 'booking site',
availableFrom: 'Available from',
unavailableNoDate: 'This vehicle is temporarily unavailable for new reservations.',
unavailable: Marketplace unavailable,
unavailableTitle: Vehicle details are temporarily unavailable.,
unavailableBody: The marketplace frontend is running, but the backing database is not reachable right now.,
backToFleet: Back to company fleet,
noPhotos: No photos available.,
rentalCompany: Rental company,
perDay: / day,
availableFrom: Available from,
unavailableNoDate: This vehicle is temporarily unavailable for new reservations.,
},
fr: {
unavailable: 'Marketplace indisponible',
unavailableTitle: 'Les détails du véhicule sont temporairement indisponibles.',
unavailableBody: 'Le frontend marketplace fonctionne, mais la base de données nest pas accessible pour le moment.',
backToFleet: 'Retour à la flotte',
noPhotos: 'Aucune photo disponible.',
rentalCompany: 'Société de location',
perDay: '/ jour',
continueTo: 'Continuer vers le site de réservation de',
bookingSite: '',
availableFrom: 'Disponible à partir du',
unavailableNoDate: 'Ce véhicule est temporairement indisponible pour les nouvelles réservations.',
unavailable: Marketplace indisponible,
unavailableTitle: Les détails du véhicule sont temporairement indisponibles.,
unavailableBody: Le frontend marketplace fonctionne, mais la base de données nest pas accessible pour le moment.,
backToFleet: Retour à la flotte,
noPhotos: Aucune photo disponible.,
rentalCompany: Société de location,
perDay: / jour,
availableFrom: Disponible à partir du,
unavailableNoDate: Ce véhicule est temporairement indisponible pour les nouvelles réservations.,
},
ar: {
unavailable: 'السوق غير متاح',
unavailableTitle: 'تفاصيل السيارة غير متاحة مؤقتاً.',
unavailableBody: 'واجهة السوق تعمل، لكن قاعدة البيانات الخلفية غير متاحة حالياً.',
backToFleet: 'العودة إلى أسطول الشركة',
noPhotos: 'لا توجد صور متاحة.',
rentalCompany: 'شركة تأجير',
perDay: '/ يوم',
continueTo: 'الانتقال إلى موقع حجز',
bookingSite: '',
availableFrom: 'متاح ابتداءً من',
unavailableNoDate: 'هذه السيارة غير متاحة مؤقتاً للحجوزات الجديدة.',
unavailable: السوق غير متاح,
unavailableTitle: تفاصيل السيارة غير متاحة مؤقتاً.,
unavailableBody: واجهة السوق تعمل، لكن قاعدة البيانات الخلفية غير متاحة حالياً.,
backToFleet: العودة إلى أسطول الشركة,
noPhotos: لا توجد صور متاحة.,
rentalCompany: شركة تأجير,
perDay: / يوم,
availableFrom: متاح ابتداءً من,
unavailableNoDate: هذه السيارة غير متاحة مؤقتاً للحجوزات الجديدة.,
},
}[language]
const vehicle = await marketplaceFetchOrDefault<VehicleDetail | null>(`/marketplace/${params.slug}/vehicles/${params.id}`, null)
@@ -90,7 +85,6 @@ export default async function VehicleDetailPage({ params }: { params: { slug: st
)
}
const destination = `https://${params.slug}.RentalDriveGo.com/book?vehicleId=${vehicle.id}&ref=marketplace`
const availabilityDate = vehicle.nextAvailableAt
? new Date(vehicle.nextAvailableAt).toLocaleDateString('en-GB', { year: 'numeric', month: 'long', day: 'numeric' })
: null
@@ -109,25 +103,33 @@ export default async function VehicleDetailPage({ params }: { params: { slug: st
{vehicle.photos.length === 0 && <div className="card p-8 text-sm text-stone-500">{dict.noPhotos}</div>}
</div>
</section>
<aside className="card p-6 h-fit">
<p className="text-xs uppercase tracking-[0.18em] text-stone-500">{vehicle.company.brand?.displayName ?? dict.rentalCompany}</p>
<h1 className="mt-3 text-4xl font-black text-stone-900">{vehicle.make} {vehicle.model}</h1>
<p className="mt-3 text-stone-600">{vehicle.year} · {vehicle.category} · {vehicle.transmission}</p>
<p className="mt-6 text-3xl font-black text-stone-900">{formatCurrency(vehicle.dailyRate, 'MAD')}<span className="text-base font-medium text-stone-500"> {dict.perDay}</span></p>
{!vehicle.availability && (
<div className="mt-4 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
{availabilityDate ? `${dict.availableFrom} ${availabilityDate}` : dict.unavailableNoDate}
<aside className="card p-6 h-fit space-y-6">
<div>
<p className="text-xs uppercase tracking-[0.18em] text-stone-500">{vehicle.company.brand?.displayName ?? dict.rentalCompany}</p>
<h1 className="mt-3 text-4xl font-black text-stone-900">{vehicle.make} {vehicle.model}</h1>
<p className="mt-3 text-stone-600">{vehicle.year} · {vehicle.category} · {vehicle.transmission}</p>
<p className="mt-6 text-3xl font-black text-stone-900">{formatCurrency(vehicle.dailyRate, 'MAD')}<span className="text-base font-medium text-stone-500"> {dict.perDay}</span></p>
{!vehicle.availability && (
<div className="mt-4 rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
{availabilityDate ? `${dict.availableFrom} ${availabilityDate}` : dict.unavailableNoDate}
</div>
)}
<div className="mt-6 flex flex-wrap gap-2">
{vehicle.features.map((feature) => (
<span key={feature} className="rounded-full bg-stone-100 px-3 py-1 text-xs font-medium text-stone-700">{feature}</span>
))}
</div>
)}
<div className="mt-6 flex flex-wrap gap-2">
{vehicle.features.map((feature) => (
<span key={feature} className="rounded-full bg-stone-100 px-3 py-1 text-xs font-medium text-stone-700">{feature}</span>
))}
</div>
<div className="mt-8 space-y-3">
<Link href={destination} className="block rounded-full bg-stone-900 px-6 py-3 text-center text-sm font-semibold text-white">{dict.continueTo} {vehicle.company.brand?.displayName ?? dict.rentalCompany} {dict.bookingSite}</Link>
<Link href={`/explore/${params.slug}`} className="block rounded-full border border-stone-300 px-6 py-3 text-center text-sm font-semibold text-stone-700">{dict.backToFleet}</Link>
</div>
{vehicle.availability ? (
<BookingForm
vehicleId={vehicle.id}
companySlug={params.slug}
dailyRate={vehicle.dailyRate}
/>
) : null}
<Link href={`/explore/${params.slug}`} className="block rounded-full border border-stone-300 px-6 py-3 text-center text-sm font-semibold text-stone-700">{dict.backToFleet}</Link>
</aside>
</div>
</main>
+3 -3
View File
@@ -102,7 +102,7 @@ export default async function ExplorePage({ searchParams }: { searchParams?: Rec
firstName: 'First name',
lastName: 'Last name',
email: 'Email address',
phone: 'Phone number (optional)',
phone: 'Phone number',
notes: 'Additional notes (optional)',
cancel: 'Cancel',
submitRequest: 'Send reservation request',
@@ -160,7 +160,7 @@ export default async function ExplorePage({ searchParams }: { searchParams?: Rec
firstName: 'Prénom',
lastName: 'Nom',
email: 'Adresse e-mail',
phone: 'Numéro de téléphone (optionnel)',
phone: 'Numéro de téléphone',
notes: 'Notes supplémentaires (optionnel)',
cancel: 'Annuler',
submitRequest: 'Envoyer la demande',
@@ -218,7 +218,7 @@ export default async function ExplorePage({ searchParams }: { searchParams?: Rec
firstName: 'الاسم الأول',
lastName: 'اسم العائلة',
email: 'البريد الإلكتروني',
phone: 'رقم الهاتف (اختياري)',
phone: 'رقم الهاتف',
notes: 'ملاحظات إضافية (اختياري)',
cancel: 'إلغاء',
submitRequest: 'إرسال طلب الحجز',