import { useEffect, useMemo, useRef, useState } from "react"; import { ArrowLeft, ArrowRight, ChevronRight, MapPin, Search, Truck, X, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import type { RegionalInventoryStats } from "../types"; import { Picker, unique } from "./shared"; import { VehicleDetails } from "./VehicleDetails"; import { INVENTORY_TYPES, inventoryModel, inventoryTotals, scopedInventorySelection, } from "./inventory-view"; import { inventoryBrowseRows, inventoryBrowseUrl, inventoryModels, inventoryPlaces, inventoryTypeRows, readInventoryBrowse, type InventoryBrowseState, } from "./inventory-explorer"; const fmt = (value: number) => value.toLocaleString(); const regionName = (value: string | null) => value || "未归属地区"; const cityName = (value: string | null) => value || "未归属城市"; export function InventoryExplorer({ inventory, subject, }: { inventory: RegionalInventoryStats[]; subject: string | null; }) { const [state, setState] = useState(() => readInventoryBrowse(window.location.search), ); const sectionRef = useRef(null); const headingRef = useRef(null); const positions = useRef( new Map< string, { scroll: [Element, number][]; y: number; focus?: string } >(), ); const currentUrl = useRef(window.location.href); const locationKey = JSON.stringify([state.region, state.city]); const lastLocation = useRef(locationKey); const remember = () => { const scroll: [Element, number][] = []; let element = sectionRef.current?.parentElement; while (element) { if (element.scrollHeight > element.clientHeight) scroll.push([element, element.scrollTop]); element = element.parentElement; } positions.current.set(currentUrl.current, { scroll, y: window.scrollY, focus: (document.activeElement as HTMLElement | null)?.dataset .inventoryKey, }); }; useEffect(() => { const onBack = () => { currentUrl.current = window.location.href; setState(readInventoryBrowse(window.location.search)); }; window.addEventListener("popstate", onBack); return () => window.removeEventListener("popstate", onBack); }, []); useEffect(() => { if (lastLocation.current === locationKey) return; lastLocation.current = locationKey; const saved = positions.current.get(currentUrl.current); if (saved) { const target = saved.focus ? Array.from( sectionRef.current?.querySelectorAll( "[data-inventory-key]", ) || [], ).find((element) => element.dataset.inventoryKey === saved.focus) : null; (target || headingRef.current)?.focus({ preventScroll: true }); saved.scroll.forEach(([element, top]) => { element.scrollTop = top; }); window.scrollTo({ top: saved.y, behavior: "instant" }); } else { headingRef.current?.focus({ preventScroll: true }); headingRef.current?.scrollIntoView({ block: "nearest", behavior: "instant", }); } }, [locationKey]); const navigate = (change: Partial, replace = false) => { remember(); const next = { ...state, ...change }; const url = inventoryBrowseUrl(window.location.href, next); if (url === window.location.href) return; if (replace) window.history.replaceState(window.history.state, "", url); else window.history.pushState( { ...window.history.state, inventoryParent: window.location.href }, "", url, ); currentUrl.current = url; setState(next); }; const goBack = () => { const next = state.model !== null ? { model: null } : state.city !== null ? { city: null, model: null } : { region: null, city: null, model: null }; const url = inventoryBrowseUrl(window.location.href, { ...state, ...next }); if (window.history.state?.inventoryParent === url) window.history.back(); else navigate(next); }; const scoped = useMemo( () => inventoryBrowseRows(inventory, state), [inventory, state], ); const rows = useMemo( () => inventoryTypeRows(scoped, state.type), [scoped, state.type], ); const total = inventoryTotals(rows); const places = inventoryPlaces( scoped, state.region === null ? "region" : "city", state.type, ); const models = useMemo(() => inventoryModels(rows), [rows]); const chosenModel = models.find((model) => model.key === state.model); const label = [ state.region === null ? "全部地区" : regionName(state.region), state.city === null ? "" : cityName(state.city), state.type || "全部车型", ] .filter(Boolean) .join(" / "); const selection = useMemo( () => chosenModel ? scopedInventorySelection({ rows: chosenModel.rows, label: `${label} / ${chosenModel.brand || "未标注品牌"} / ${chosenModel.model}`, }) : null, [chosenModel, label], ); const level = state.region === null ? 0 : state.city === null ? 1 : 2; const hasFilters = Boolean(state.query || state.brand); return (

库存找车

选地区、选城市,快速找到对应车辆

    {["选地区", "选城市", "查车辆"].map((title, index) => (
  1. {index + 1} {title} {index < 2 &&
  2. ))}
{level > 0 && ( )}

{state.city !== null ? cityName(state.city) : state.region !== null ? regionName(state.region) : "地区库存"}

{state.type || "全部车型"} {hasFilters ? " · 已筛选" : ""} ·{" "} {state.city !== null ? `${models.length} 种型号` : `${places.filter((place) => place.total.quantity > 0).length} 个${level === 0 ? "地区" : "城市"}有库存`}

{fmt(total.quantity)} 库存 / 台
找什么车型 数量为当前范围库存
{["", ...INVENTORY_TYPES].map((type) => ( ))}
row.brand))} change={(brand) => navigate({ brand, model: null })} />
{level === 0 ? "选择地区" : level === 1 ? "选择城市" : "选择型号查看车辆"} {hasFilters ? ( ) : ( {level < 2 ? "按库存从多到少" : "点击进入车辆明细"} )}
{total.quantity === 0 && (
)} {level < 2 ? (
{places.map((place) => ( ))}
) : (
{models.map((model) => ( ))}
)} {state.model !== null && !chosenModel && (
该型号已不在当前库存范围
)}
库存包含待交车、异动车辆,不代表均可立即交付。 「其他」包含挂车及未单列车型。
); }