fix the errors in dashboard

This commit is contained in:
root
2026-05-23 19:03:15 -04:00
parent c6cebdd125
commit f5292f8b6c
29 changed files with 1085 additions and 126 deletions
@@ -324,7 +324,10 @@ export default function OffersPage() {
function load() {
apiFetch<Offer[]>('/offers')
.then((data) => setOffers(data ?? []))
.then((data) => {
setOffers(Array.isArray(data) ? data : [])
setError(null)
})
.catch((err) => setError(err.message))
}
@@ -334,7 +337,7 @@ export default function OffersPage() {
setLoadingFleet(true)
try {
const data = await apiFetch<FleetVehicle[]>('/vehicles?pageSize=200')
setFleet(data ?? [])
setFleet(Array.isArray(data) ? data : [])
} catch {
// non-fatal; vehicle picker just stays empty
} finally {
@@ -363,7 +366,7 @@ export default function OffersPage() {
const detail = await apiFetch<Offer>(`/offers/${offer.id}`)
setForm((prev) => ({
...prev,
vehicleIds: detail.vehicles?.map((v) => v.vehicleId) ?? [],
vehicleIds: Array.isArray(detail?.vehicles) ? detail.vehicles.map((v) => v.vehicleId) : [],
}))
} catch {
// leave vehicleIds as empty if fetch fails
@@ -117,11 +117,19 @@ export default function SignInPageClient({ embedded = false }: { embedded?: bool
changed = true
}
// useSearchParams() can return empty params during hydration without a Suspense boundary.
// Always preserve embedded=1 when the component was server-rendered as embedded so
// the router.replace doesn't strip it and trigger an unintended redirect.
if (embedded && params.get('embedded') !== '1') {
params.set('embedded', '1')
changed = true
}
if (!changed) return
const nextQuery = params.toString()
router.replace(nextQuery ? `${pathname}?${nextQuery}` : pathname, { scroll: false })
}, [language, pathname, router, searchParams, theme])
}, [embedded, language, pathname, router, searchParams, theme])
useEffect(() => {
const currentPath = window.location.pathname + (window.location.search || '')
@@ -1,7 +1,16 @@
import { Suspense } from 'react'
import { headers } from 'next/headers'
import { redirect } from 'next/navigation'
import SignInPageClient from './SignInPageClient'
const MARKETPLACE_URL = process.env.NEXT_PUBLIC_MARKETPLACE_URL ?? 'http://localhost:3000'
function isInternalHost(host: string | null): boolean {
if (!host) return false
const hostname = host.split(':')[0]?.toLowerCase()
return ['host.docker.internal', 'dashboard', 'admin', 'api', 'marketplace'].includes(hostname)
}
export default async function SignInPage({
searchParams,
}: {
@@ -13,6 +22,7 @@ export default async function SignInPage({
if (!embedded) {
const requestHeaders = headers()
const host = requestHeaders.get('host')
const forwardedHost = requestHeaders.get('x-forwarded-host')
const proto = requestHeaders.get('x-forwarded-proto') ?? 'http'
const query = new URLSearchParams()
@@ -25,12 +35,22 @@ export default async function SignInPage({
}
}
const destination = host
? `${proto}://${host}/sign-in${query.size ? `?${query.toString()}` : ''}`
: `${process.env.NEXT_PUBLIC_MARKETPLACE_URL ?? 'http://localhost:3000'}/sign-in${query.size ? `?${query.toString()}` : ''}`
const marketplaceOrigin = new URL(MARKETPLACE_URL)
const publicHost = forwardedHost && !isInternalHost(forwardedHost)
? forwardedHost
: host && !isInternalHost(host)
? host
: null
redirect(destination)
if (!publicHost || publicHost === marketplaceOrigin.host) {
const destination = `${MARKETPLACE_URL}/sign-in${query.size ? `?${query.toString()}` : ''}`
redirect(destination)
}
}
return <SignInPageClient embedded={embedded} />
return (
<Suspense fallback={null}>
<SignInPageClient embedded={embedded} />
</Suspense>
)
}