import React, { useEffect, useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { __ } from "../../lib/i18n"; import { apiClient } from "../../lib/api-client"; import { Select } from "../ui/select"; import { Input } from "../ui/input"; import { Button } from "../ui/button"; import { Loader2, Search, X } from "lucide-react"; type ApplicableValue = "all" | "specific_trips"; interface ApplicableTripSelectorProps { value: ApplicableValue; onValueChange: (value: ApplicableValue) => void; selectedTripIds: number[]; onTripIdsChange: (ids: number[]) => void; description?: string; placeholder?: string; helperText?: string; disabled?: boolean; } interface TripOption { id: number; label: string; } const toTripOption = (item: any): TripOption => { const id = Number(item?.id || 0); const label = item?.title || item?.name || item?.slug || `Trip #${id}`; return { id, label }; }; export const ApplicableTripSelector: React.FC = ({ value, onValueChange, selectedTripIds, onTripIdsChange, description = __( "Control whether this applies to all trips or only selected trips.", ), placeholder = __("Click to select trips..."), helperText = __("Only the selected trips will be eligible."), disabled = false, }) => { const [showDropdown, setShowDropdown] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [debouncedSearch, setDebouncedSearch] = useState(""); useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(searchTerm); }, 300); return () => clearTimeout(timer); }, [searchTerm]); const { data: trips = [], isLoading, isFetching, } = useQuery({ queryKey: ["applicable-trips", debouncedSearch], queryFn: async () => { const params: Record = { per_page: 100 }; if (debouncedSearch.trim()) { params.search = debouncedSearch.trim(); } const response = await apiClient.get("/trips", { params }); const items = response?.data?.data || response?.data || response || []; return Array.isArray(items) ? items.map(toTripOption) : []; }, enabled: value === "specific_trips", staleTime: 5 * 60 * 1000, }); // Fetch selected trip details using React Query const { data: selectedTripsData = [] } = useQuery({ queryKey: ["selected-trips", selectedTripIds], queryFn: async () => { if (selectedTripIds.length === 0) return []; // Try using the list endpoint with include parameter instead of individual calls try { const response = await apiClient.get("/trips", { params: { per_page: 100, include: selectedTripIds.join(","), // Try to include specific IDs }, }); const items = response?.data?.data || response?.data || response || []; const trips = Array.isArray(items) ? items.map(toTripOption) : []; return trips; } catch (error) { console.warn("List API failed, trying individual calls:", error); // Fallback to individual calls const results = await Promise.all( selectedTripIds.map(async (id) => { try { const response = await apiClient.get(`/trips/${id}`); if (response?.data) { const trip = toTripOption(response.data); return trip; } } catch (error) { console.warn(`Failed to fetch trip ${id}:`, error); } return { id, label: `Trip #${id}` }; }), ); return results; } }, enabled: selectedTripIds.length > 0, staleTime: 10 * 60 * 1000, // Cache for 10 minutes }); const tripLookup = useMemo(() => { const map = new Map(); // Add search results trips.forEach((trip) => { map.set(trip.id, trip.label); }); // Add selected trips data selectedTripsData.forEach((trip) => { map.set(trip.id, trip.label); }); return map; }, [trips, selectedTripsData]); const selectedChips = useMemo(() => { const chips = selectedTripIds.map((id) => ({ id, label: tripLookup.get(id) || `Trip #${id}`, })); return chips; }, [selectedTripIds, tripLookup]); const toggleTrip = (tripId: number, checked: boolean) => { if (checked) { if (!selectedTripIds.includes(tripId)) { onTripIdsChange([...selectedTripIds, tripId]); } } else { onTripIdsChange(selectedTripIds.filter((id) => id !== tripId)); } }; const clearTrips = (e?: React.MouseEvent) => { e?.stopPropagation(); onTripIdsChange([]); }; const handleModeChange = (newValue: ApplicableValue) => { onValueChange(newValue); if (newValue === "all" && selectedTripIds.length > 0) { onTripIdsChange([]); } }; return (

{description}

{value === "specific_trips" && (
!disabled && setShowDropdown((prev) => !prev)} > {selectedChips.length > 0 ? (
{selectedChips.map((chip) => ( {chip.label} #{chip.id} ))}
) : ( {placeholder} )}
{helperText && (

{helperText}

)} {showDropdown && !disabled && (
setSearchTerm(e.target.value)} placeholder={__("Search trips...")} className="pl-8" /> {isFetching && ( )}
{isLoading ? (
{__("Loading trips...")}
) : trips.length > 0 ? (
{trips.map((trip) => { const checked = selectedTripIds.includes(trip.id); return ( ); })}
) : (
{searchTerm ? __("No trips found") : __("No trips available")}
)}
{selectedTripIds.length} {__("selected")}
)}
)}
); };