|
@@ -37,6 +37,8 @@ interface VehicleComboboxProps {
|
|
|
initialVehicle?: VehicleOption | null;
|
|
initialVehicle?: VehicleOption | null;
|
|
|
placeholder?: string;
|
|
placeholder?: string;
|
|
|
noneLabel?: string;
|
|
noneLabel?: string;
|
|
|
|
|
+ /** When set, only this customer's vehicles are offered */
|
|
|
|
|
+ customerId?: string;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function formatVehicle(v: VehicleOption) {
|
|
function formatVehicle(v: VehicleOption) {
|
|
@@ -49,6 +51,7 @@ export function VehicleCombobox({
|
|
|
initialVehicle,
|
|
initialVehicle,
|
|
|
placeholder = "Select vehicle...",
|
|
placeholder = "Select vehicle...",
|
|
|
noneLabel = "None",
|
|
noneLabel = "None",
|
|
|
|
|
+ customerId,
|
|
|
}: VehicleComboboxProps) {
|
|
}: VehicleComboboxProps) {
|
|
|
const [open, setOpen] = useState(false);
|
|
const [open, setOpen] = useState(false);
|
|
|
const [search, setSearch] = useState("");
|
|
const [search, setSearch] = useState("");
|
|
@@ -62,6 +65,10 @@ export function VehicleCombobox({
|
|
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
const listRef = useRef<HTMLDivElement | null>(null);
|
|
const listRef = useRef<HTMLDivElement | null>(null);
|
|
|
const loadingMoreRef = useRef(false);
|
|
const loadingMoreRef = useRef(false);
|
|
|
|
|
+ const valueRef = useRef(value);
|
|
|
|
|
+ valueRef.current = value;
|
|
|
|
|
+ const onChangeRef = useRef(onChange);
|
|
|
|
|
+ onChangeRef.current = onChange;
|
|
|
|
|
|
|
|
const mapVehicle = (v: VehicleOption) => ({
|
|
const mapVehicle = (v: VehicleOption) => ({
|
|
|
id: v.id,
|
|
id: v.id,
|
|
@@ -81,10 +88,11 @@ export function VehicleCombobox({
|
|
|
loadingMoreRef.current = true;
|
|
loadingMoreRef.current = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const result = await searchVehicles(query || undefined, PAGE_SIZE, offset);
|
|
|
|
|
|
|
+ const result = await searchVehicles(query || undefined, PAGE_SIZE, offset, customerId || undefined);
|
|
|
|
|
|
|
|
|
|
+ let mapped: VehicleOption[] = [];
|
|
|
if (result.success && result.data) {
|
|
if (result.success && result.data) {
|
|
|
- const mapped = result.data.map(mapVehicle);
|
|
|
|
|
|
|
+ mapped = result.data.map(mapVehicle);
|
|
|
if (append) {
|
|
if (append) {
|
|
|
setOptions((prev) => [...prev, ...mapped]);
|
|
setOptions((prev) => [...prev, ...mapped]);
|
|
|
} else {
|
|
} else {
|
|
@@ -96,14 +104,27 @@ export function VehicleCombobox({
|
|
|
setSearching(false);
|
|
setSearching(false);
|
|
|
setLoadingMore(false);
|
|
setLoadingMore(false);
|
|
|
loadingMoreRef.current = false;
|
|
loadingMoreRef.current = false;
|
|
|
|
|
+ return mapped;
|
|
|
},
|
|
},
|
|
|
- []
|
|
|
|
|
|
|
+ [customerId]
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // Prefetch on mount
|
|
|
|
|
|
|
+ // Prefetch on mount and whenever the customer filter changes. When a
|
|
|
|
|
+ // customer filter is active and they own exactly one vehicle, select it
|
|
|
|
|
+ // automatically so the user doesn't have to.
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- loadOptions();
|
|
|
|
|
- }, [loadOptions]);
|
|
|
|
|
|
|
+ let cancelled = false;
|
|
|
|
|
+ loadOptions().then((opts) => {
|
|
|
|
|
+ if (cancelled || !customerId) return;
|
|
|
|
|
+ if (opts.length === 1 && !valueRef.current) {
|
|
|
|
|
+ setSelected(opts[0]);
|
|
|
|
|
+ onChangeRef.current(opts[0].id, opts[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ cancelled = true;
|
|
|
|
|
+ };
|
|
|
|
|
+ }, [loadOptions, customerId]);
|
|
|
|
|
|
|
|
// Debounced search — resets to page 0
|
|
// Debounced search — resets to page 0
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
@@ -125,6 +146,11 @@ export function VehicleCombobox({
|
|
|
if (initialVehicle) setSelected(initialVehicle);
|
|
if (initialVehicle) setSelected(initialVehicle);
|
|
|
}, [initialVehicle]);
|
|
}, [initialVehicle]);
|
|
|
|
|
|
|
|
|
|
+ // Parent can clear the selection (e.g. customer changed); drop the label too
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (!value) setSelected(null);
|
|
|
|
|
+ }, [value]);
|
|
|
|
|
+
|
|
|
// Infinite scroll
|
|
// Infinite scroll
|
|
|
const handleScroll = useCallback(() => {
|
|
const handleScroll = useCallback(() => {
|
|
|
const el = listRef.current;
|
|
const el = listRef.current;
|