| 1 |
import { r as reactExports, u as useQuery, j as jsxRuntimeExports, aP as Search, E as Loader2, ay as X } from "./react-vendor-CithbthY.js"; |
| 2 |
import { _ as __, I as Input, a as apiClient } from "./index-Dec_weLB.js"; |
| 3 |
import { S as Select, B as Button } from "../../admin/dist/js/app.js"; |
| 4 |
const toTripOption = (item) => { |
| 5 |
const id = Number((item == null ? void 0 : item.id) || 0); |
| 6 |
const label = (item == null ? void 0 : item.title) || (item == null ? void 0 : item.name) || (item == null ? void 0 : item.slug) || `Trip #${id}`; |
| 7 |
return { id, label }; |
| 8 |
}; |
| 9 |
const ApplicableTripSelector = ({ |
| 10 |
value, |
| 11 |
onValueChange, |
| 12 |
selectedTripIds, |
| 13 |
onTripIdsChange, |
| 14 |
description = __( |
| 15 |
"Control whether this applies to all trips or only selected trips." |
| 16 |
), |
| 17 |
placeholder = __("Click to select trips..."), |
| 18 |
helperText = __("Only the selected trips will be eligible."), |
| 19 |
disabled = false |
| 20 |
}) => { |
| 21 |
const [showDropdown, setShowDropdown] = reactExports.useState(false); |
| 22 |
const [searchTerm, setSearchTerm] = reactExports.useState(""); |
| 23 |
const [debouncedSearch, setDebouncedSearch] = reactExports.useState(""); |
| 24 |
reactExports.useEffect(() => { |
| 25 |
const timer = setTimeout(() => { |
| 26 |
setDebouncedSearch(searchTerm); |
| 27 |
}, 300); |
| 28 |
return () => clearTimeout(timer); |
| 29 |
}, [searchTerm]); |
| 30 |
const { |
| 31 |
data: trips = [], |
| 32 |
isLoading, |
| 33 |
isFetching |
| 34 |
} = useQuery({ |
| 35 |
queryKey: ["applicable-trips", debouncedSearch], |
| 36 |
queryFn: async () => { |
| 37 |
var _a; |
| 38 |
const params = { per_page: 100 }; |
| 39 |
if (debouncedSearch.trim()) { |
| 40 |
params.search = debouncedSearch.trim(); |
| 41 |
} |
| 42 |
const response = await apiClient.get("/trips", { params }); |
| 43 |
const items = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.data) || (response == null ? void 0 : response.data) || response || []; |
| 44 |
return Array.isArray(items) ? items.map(toTripOption) : []; |
| 45 |
}, |
| 46 |
enabled: value === "specific_trips", |
| 47 |
staleTime: 5 * 60 * 1e3 |
| 48 |
}); |
| 49 |
const { data: selectedTripsData = [] } = useQuery({ |
| 50 |
queryKey: ["selected-trips", selectedTripIds], |
| 51 |
queryFn: async () => { |
| 52 |
var _a; |
| 53 |
if (selectedTripIds.length === 0) return []; |
| 54 |
try { |
| 55 |
const response = await apiClient.get("/trips", { |
| 56 |
params: { |
| 57 |
per_page: 100, |
| 58 |
include: selectedTripIds.join(",") |
| 59 |
// Try to include specific IDs |
| 60 |
} |
| 61 |
}); |
| 62 |
const items = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.data) || (response == null ? void 0 : response.data) || response || []; |
| 63 |
const trips2 = Array.isArray(items) ? items.map(toTripOption) : []; |
| 64 |
return trips2; |
| 65 |
} catch (error) { |
| 66 |
console.warn("List API failed, trying individual calls:", error); |
| 67 |
const results = await Promise.all( |
| 68 |
selectedTripIds.map(async (id) => { |
| 69 |
try { |
| 70 |
const response = await apiClient.get(`/trips/${id}`); |
| 71 |
if (response == null ? void 0 : response.data) { |
| 72 |
const trip = toTripOption(response.data); |
| 73 |
return trip; |
| 74 |
} |
| 75 |
} catch (error2) { |
| 76 |
console.warn(`Failed to fetch trip ${id}:`, error2); |
| 77 |
} |
| 78 |
return { id, label: `Trip #${id}` }; |
| 79 |
}) |
| 80 |
); |
| 81 |
return results; |
| 82 |
} |
| 83 |
}, |
| 84 |
enabled: selectedTripIds.length > 0, |
| 85 |
staleTime: 10 * 60 * 1e3 |
| 86 |
// Cache for 10 minutes |
| 87 |
}); |
| 88 |
const tripLookup = reactExports.useMemo(() => { |
| 89 |
const map = /* @__PURE__ */ new Map(); |
| 90 |
trips.forEach((trip) => { |
| 91 |
map.set(trip.id, trip.label); |
| 92 |
}); |
| 93 |
selectedTripsData.forEach((trip) => { |
| 94 |
map.set(trip.id, trip.label); |
| 95 |
}); |
| 96 |
return map; |
| 97 |
}, [trips, selectedTripsData]); |
| 98 |
const selectedChips = reactExports.useMemo(() => { |
| 99 |
const chips = selectedTripIds.map((id) => ({ |
| 100 |
id, |
| 101 |
label: tripLookup.get(id) || `Trip #${id}` |
| 102 |
})); |
| 103 |
return chips; |
| 104 |
}, [selectedTripIds, tripLookup]); |
| 105 |
const toggleTrip = (tripId, checked) => { |
| 106 |
if (checked) { |
| 107 |
if (!selectedTripIds.includes(tripId)) { |
| 108 |
onTripIdsChange([...selectedTripIds, tripId]); |
| 109 |
} |
| 110 |
} else { |
| 111 |
onTripIdsChange(selectedTripIds.filter((id) => id !== tripId)); |
| 112 |
} |
| 113 |
}; |
| 114 |
const clearTrips = (e) => { |
| 115 |
e == null ? void 0 : e.stopPropagation(); |
| 116 |
onTripIdsChange([]); |
| 117 |
}; |
| 118 |
const handleModeChange = (newValue) => { |
| 119 |
onValueChange(newValue); |
| 120 |
if (newValue === "all" && selectedTripIds.length > 0) { |
| 121 |
onTripIdsChange([]); |
| 122 |
} |
| 123 |
}; |
| 124 |
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [ |
| 125 |
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ |
| 126 |
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-2", children: description }), |
| 127 |
/* @__PURE__ */ jsxRuntimeExports.jsxs( |
| 128 |
Select, |
| 129 |
{ |
| 130 |
value, |
| 131 |
onChange: (e) => handleModeChange(e.target.value), |
| 132 |
disabled, |
| 133 |
children: [ |
| 134 |
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "all", children: __("All Trips") }), |
| 135 |
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "specific_trips", children: __("Specific Trips") }) |
| 136 |
] |
| 137 |
} |
| 138 |
) |
| 139 |
] }), |
| 140 |
value === "specific_trips" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 relative", children: [ |
| 141 |
/* @__PURE__ */ jsxRuntimeExports.jsx( |
| 142 |
"div", |
| 143 |
{ |
| 144 |
className: `border border-gray-300 dark:border-gray-600 rounded-lg p-2 cursor-pointer hover:border-gray-400 dark:hover:border-gray-500 bg-white dark:bg-gray-800 ${disabled ? "opacity-70 cursor-not-allowed" : ""}`, |
| 145 |
onClick: () => !disabled && setShowDropdown((prev) => !prev), |
| 146 |
children: selectedChips.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [ |
| 147 |
selectedChips.map((chip) => /* @__PURE__ */ jsxRuntimeExports.jsxs( |
| 148 |
"span", |
| 149 |
{ |
| 150 |
className: "inline-flex items-center justify-center gap-1 px-2 py-1.5 text-[11px] font-medium bg-blue-50 text-blue-700 border border-blue-100 rounded-full h-6 min-w-[24px]", |
| 151 |
children: [ |
| 152 |
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: chip.label }), |
| 153 |
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] text-gray-500 font-normal flex-shrink-0", children: [ |
| 154 |
"#", |
| 155 |
chip.id |
| 156 |
] }), |
| 157 |
/* @__PURE__ */ jsxRuntimeExports.jsx( |
| 158 |
"button", |
| 159 |
{ |
| 160 |
type: "button", |
| 161 |
onClick: (e) => { |
| 162 |
e.stopPropagation(); |
| 163 |
toggleTrip(chip.id, false); |
| 164 |
}, |
| 165 |
className: "hover:text-blue-600 text-[10px] flex-shrink-0 leading-none", |
| 166 |
children: "×" |
| 167 |
} |
| 168 |
) |
| 169 |
] |
| 170 |
}, |
| 171 |
chip.id |
| 172 |
)), |
| 173 |
/* @__PURE__ */ jsxRuntimeExports.jsx( |
| 174 |
Button, |
| 175 |
{ |
| 176 |
type: "button", |
| 177 |
variant: "ghost", |
| 178 |
size: "sm", |
| 179 |
onClick: clearTrips, |
| 180 |
className: "text-xs text-red-500 hover:text-red-600 px-2 py-0 h-6 flex items-center", |
| 181 |
children: __("Clear") |
| 182 |
} |
| 183 |
) |
| 184 |
] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: placeholder }) |
| 185 |
} |
| 186 |
), |
| 187 |
helperText && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: helperText }), |
| 188 |
showDropdown && !disabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg", children: [ |
| 189 |
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-2 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [ |
| 190 |
/* @__PURE__ */ jsxRuntimeExports.jsx( |
| 191 |
Input, |
| 192 |
{ |
| 193 |
type: "text", |
| 194 |
value: searchTerm, |
| 195 |
onChange: (e) => setSearchTerm(e.target.value), |
| 196 |
placeholder: __("Search trips..."), |
| 197 |
className: "pl-8" |
| 198 |
} |
| 199 |
), |
| 200 |
/* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "w-4 h-4 text-gray-400 absolute left-2.5 top-1/2 -translate-y-1/2" }), |
| 201 |
isFetching && /* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "w-4 h-4 animate-spin text-gray-400 absolute right-2 top-1/2 -translate-y-1/2" }) |
| 202 |
] }) }), |
| 203 |
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-64 overflow-y-auto", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 text-center text-sm text-gray-500 dark:text-gray-400", children: [ |
| 204 |
/* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "w-5 h-5 animate-spin mx-auto mb-2" }), |
| 205 |
__("Loading trips...") |
| 206 |
] }) : trips.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-700", children: trips.map((trip) => { |
| 207 |
const checked = selectedTripIds.includes(trip.id); |
| 208 |
return /* @__PURE__ */ jsxRuntimeExports.jsxs( |
| 209 |
"label", |
| 210 |
{ |
| 211 |
className: "flex items-center gap-3 px-3 py-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/40", |
| 212 |
onClick: (e) => e.stopPropagation(), |
| 213 |
children: [ |
| 214 |
/* @__PURE__ */ jsxRuntimeExports.jsx( |
| 215 |
"input", |
| 216 |
{ |
| 217 |
type: "checkbox", |
| 218 |
checked, |
| 219 |
onChange: (e) => toggleTrip(trip.id, e.target.checked), |
| 220 |
className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" |
| 221 |
} |
| 222 |
), |
| 223 |
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 text-sm text-gray-900 dark:text-white truncate", children: trip.label }), |
| 224 |
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: [ |
| 225 |
"#", |
| 226 |
trip.id |
| 227 |
] }) |
| 228 |
] |
| 229 |
}, |
| 230 |
trip.id |
| 231 |
); |
| 232 |
}) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-center text-sm text-gray-500 dark:text-gray-400", children: searchTerm ? __("No trips found") : __("No trips available") }) }), |
| 233 |
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 border-t border-gray-200 dark:border-gray-700 flex items-center justify-between", children: [ |
| 234 |
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [ |
| 235 |
selectedTripIds.length, |
| 236 |
" ", |
| 237 |
__("selected") |
| 238 |
] }), |
| 239 |
/* @__PURE__ */ jsxRuntimeExports.jsxs( |
| 240 |
Button, |
| 241 |
{ |
| 242 |
type: "button", |
| 243 |
variant: "ghost", |
| 244 |
size: "sm", |
| 245 |
onClick: () => setShowDropdown(false), |
| 246 |
className: "text-xs", |
| 247 |
children: [ |
| 248 |
/* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "w-4 h-4 mr-1" }), |
| 249 |
__("Close") |
| 250 |
] |
| 251 |
} |
| 252 |
) |
| 253 |
] }) |
| 254 |
] }) |
| 255 |
] }) |
| 256 |
] }); |
| 257 |
}; |
| 258 |
export { |
| 259 |
ApplicableTripSelector as A |
| 260 |
}; |
| 261 |
//# sourceMappingURL=ApplicableTripSelector-BZ25j59n.js.map |
| 262 |
|