@nerdjs/sales-kit 4.0.56 → 4.0.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/entities/quote/quote.d.ts +2 -1
  2. package/dist/helpers.d.ts +9 -0
  3. package/dist/helpers.js +54 -0
  4. package/dist/helpers.js.map +1 -1
  5. package/dist/hooks/lep/lepLoadLocation.d.ts +0 -6
  6. package/dist/hooks/lep/lepLoadLocation.js +207 -177
  7. package/dist/hooks/lep/lepLoadLocation.js.map +1 -1
  8. package/dist/hooks/lep/lepShipcons.js +25 -7
  9. package/dist/hooks/lep/lepShipcons.js.map +1 -1
  10. package/dist/hooks/lep/nerdMap/nerdMap.js +28 -40
  11. package/dist/hooks/lep/nerdMap/nerdMap.js.map +1 -1
  12. package/dist/hooks/locationsForm/locationFormSales/locationFormSales.d.ts +1 -1
  13. package/dist/hooks/locationsForm/locationFormSales/locationFormSales.js +4 -3
  14. package/dist/hooks/locationsForm/locationFormSales/locationFormSales.js.map +1 -1
  15. package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.d.ts +0 -5
  16. package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.js +173 -56
  17. package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.js.map +1 -1
  18. package/dist/hooks/quote/constants.d.ts +1 -1
  19. package/dist/hooks/quote/constants.js +2 -2
  20. package/dist/hooks/quote/constants.js.map +1 -1
  21. package/dist/hooks/quote/form.js +16 -5
  22. package/dist/hooks/quote/form.js.map +1 -1
  23. package/dist/hooks/quote/helpers.d.ts +1 -0
  24. package/dist/hooks/quote/helpers.js +304 -325
  25. package/dist/hooks/quote/helpers.js.map +1 -1
  26. package/dist/hooks/quote/template.js +2 -2
  27. package/dist/hooks/quote/template.js.map +1 -1
  28. package/dist/redux/commoditiesTasksSlice/commoditiesTasksSelectors.d.ts +2 -2
  29. package/dist/redux/customerV1/customerV1Endpoints.d.ts +2 -2
  30. package/dist/redux/lep/lepSelectors.d.ts +6 -6
  31. package/dist/redux/load/loadDrawerSlice.d.ts +1 -1
  32. package/dist/redux/loadInfoTasksSlice/loadInfoTasksSelectors.d.ts +10 -10
  33. package/dist/redux/locationV1/locationV1Endpoints.d.ts +12 -12
  34. package/dist/redux/quote/quoteAction.d.ts +2 -3
  35. package/dist/redux/quote/quoteAction.js +2 -3
  36. package/dist/redux/quote/quoteAction.js.map +1 -1
  37. package/dist/redux/quote/quoteCustomerPortalEndpoints.d.ts +1 -1
  38. package/dist/redux/quote/quoteReducer.js +5 -5
  39. package/dist/redux/quote/quoteReducer.js.map +1 -1
  40. package/dist/redux/quote/quoteSelectors.d.ts +23 -23
  41. package/dist/redux/shipconsTasksSlice/shipconsTasksSelectors.d.ts +2 -2
  42. package/dist/redux/tender/tenderSelector.d.ts +7 -7
  43. package/package.json +2 -2
@@ -20,6 +20,7 @@ import { LocationForm } from "../locationsForm";
20
20
  import { FREIGHT_CLASS, LTL, SHIPPERS, TL } from "./constants";
21
21
  import { LocationFormSales } from "../locationsForm/locationFormSales/locationFormSales";
22
22
  import { unstable_debounce as debounce } from "@mui/utils";
23
+ import { getCacheByPlaceId, useGoogleMapsAutocompleteCache, } from "../../helpers";
23
24
  export const getStopsColumnDef = (dispatch, stopKind, rows, customerPortal, onEditLocationClick, errors) => {
24
25
  const date = {
25
26
  headerName: "Date",
@@ -47,7 +48,7 @@ export const getStopsColumnDef = (dispatch, stopKind, rows, customerPortal, onEd
47
48
  editable: false,
48
49
  width: 40,
49
50
  valueGetter: (_v, row) => {
50
- return rows.findIndex((r) => r.key === row.key) + 1;
51
+ return rows.findIndex((r) => r.id === row.id) + 1;
51
52
  },
52
53
  renderCell: (p) => (_jsx(Avatar, { size: "sm", variant: "soft", children: p.value })),
53
54
  },
@@ -77,25 +78,32 @@ export const getStopsColumnDef = (dispatch, stopKind, rows, customerPortal, onEd
77
78
  valueSetter(value, row) {
78
79
  if (value && typeof value != "string")
79
80
  if (typeof value != "number")
80
- if (locationEntityTypeGuard(value))
81
+ if (locationEntityTypeGuard(value)) {
81
82
  return {
82
83
  ...row,
83
84
  locationId: value.id,
84
85
  zip: value.address.zip,
86
+ placeId: undefined,
85
87
  };
86
- else
88
+ }
89
+ else {
87
90
  return {
88
91
  ...row,
89
92
  locationId: value.id,
90
93
  zip: value.zip,
94
+ placeId: undefined,
91
95
  };
92
- else
96
+ }
97
+ else {
93
98
  return {
94
99
  ...row,
95
100
  locationId: value,
101
+ placeId: undefined,
96
102
  };
97
- else
98
- return { ...row, locationId: undefined };
103
+ }
104
+ else {
105
+ return { ...row, locationId: undefined, placeId: undefined };
106
+ }
99
107
  },
100
108
  },
101
109
  {
@@ -120,7 +128,7 @@ export const getStopsColumnDef = (dispatch, stopKind, rows, customerPortal, onEd
120
128
  getActions: (p) => [
121
129
  _jsx(GridActionsCellItem, { icon: _jsx("i", { style: { fontSize: 14 }, className: "fa-sharp fa-solid fa-trash" }), label: `Delete`, onClick: (e) => {
122
130
  e.stopPropagation();
123
- dispatch(removeStop(p.row.key));
131
+ dispatch(removeStop(p.row.id));
124
132
  }, color: "inherit" }, "delete"),
125
133
  ],
126
134
  },
@@ -275,144 +283,181 @@ export const getPalletsColumnDef = (deleteAction, customerPortal, perPalletClass
275
283
  });
276
284
  return ret;
277
285
  };
278
- const ZipCodeRenderCell = ({ value, errors, }) => {
279
- const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
280
- const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
286
+ const ZipCodeRenderCell = ({ value, errors, row, id, }) => {
287
+ const apiRef = useGridApiContext();
288
+ const { getSuggestions } = useGoogleMapsAutocompleteCache();
281
289
  useEffect(() => {
282
- if (value) {
283
- fetchAutocompleteSuggestions({
290
+ let active = true;
291
+ if (!value) {
292
+ return;
293
+ }
294
+ if (!row.placeId)
295
+ getSuggestions({
284
296
  input: value,
285
- includedRegionCodes: ["US"],
286
297
  includedPrimaryTypes: ["(regions)"],
298
+ includedRegionCodes: ["US"],
287
299
  language: "en-US",
288
300
  region: "us",
289
- }).then(({ suggestions }) => {
290
- setGoogleMapsPredictions(suggestions);
301
+ }).then((suggestions) => {
302
+ if (!active || !suggestions?.[0])
303
+ return;
304
+ const place = suggestions?.[0].placePrediction?.toPlace();
305
+ if (!place)
306
+ return;
307
+ place
308
+ .fetchFields({
309
+ fields: ["addressComponents"],
310
+ })
311
+ .then((result) => {
312
+ const components = result.place.addressComponents || [];
313
+ const city = components.find((c) => c.types.includes("locality"))?.longText ||
314
+ components.find((c) => c.types.includes("postal_town"))
315
+ ?.longText ||
316
+ components.find((c) => c.types.includes("administrative_area_level_2"))?.longText ||
317
+ "";
318
+ const state = components.find((c) => c.types.includes("administrative_area_level_1"))?.shortText || "";
319
+ const zip = components.find((c) => c.types.includes("postal_code"))
320
+ ?.longText || "";
321
+ const main = city || zip;
322
+ const finalValue = main && state ? `${main}, ${state}` : main || state || "";
323
+ apiRef.current.updateRows([
324
+ {
325
+ id,
326
+ zip: finalValue,
327
+ placeId: suggestions[0].placePrediction?.placeId || undefined,
328
+ },
329
+ ]);
330
+ });
291
331
  });
292
- }
332
+ return () => {
333
+ active = false;
334
+ };
293
335
  }, [value]);
294
336
  return (_jsx(Box, { width: "100%", children: value ? (_jsx(Typography, { level: "body-sm", sx: {
295
337
  display: "inline",
296
- opacity: value || errors ? 1 : 0.5,
297
- color: (theme) => errors && !value ? theme.palette.danger[500] : undefined,
298
- }, children: googleMapsPredictions?.[0]
299
- ? googleMapsPredictions[0].placePrediction?.text.text || ""
300
- : value })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { display: "inline", level: "body-sm", sx: {
301
- opacity: value || errors ? 1 : 0.5,
302
- color: (theme) => errors && !value ? theme.palette.danger[500] : undefined,
303
- }, children: "Enter zip or city, state... " }), _jsx(Typography, { display: "inline", color: "danger", children: "*" })] })) }));
338
+ opacity: errors ? 1 : 0.5,
339
+ color: (theme) => (errors ? theme.palette.danger[500] : undefined),
340
+ }, children: value })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { level: "body-sm", display: "inline", children: "Enter zip or city, state..." }), _jsx(Typography, { display: "inline", color: "danger", children: "*" })] })) }));
304
341
  };
305
- const ZipCodeRenderEditCell = ({ value: _value, id, field, hasFocus, }) => {
342
+ export const ZipCodeRenderEditCell = ({ id, field, hasFocus, value: initialValue, row, }) => {
306
343
  const apiRef = useGridApiContext();
307
- const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
308
- const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
309
344
  const ref = useRef(null);
345
+ const { getSuggestions } = useGoogleMapsAutocompleteCache();
346
+ const [options, setOptions] = useState([]);
347
+ const [inputValue, setInputValue] = useState("");
310
348
  const [open, setOpen] = useState(false);
311
- const [value, setValue] = useState(null);
312
349
  useLayoutEffect(() => {
313
- if (hasFocus) {
350
+ if (hasFocus)
314
351
  ref.current?.focus();
315
- }
316
352
  }, [hasFocus]);
317
353
  useEffect(() => {
318
- if (_value)
319
- fetchAutocompleteSuggestions({
320
- input: _value,
321
- includedPrimaryTypes: ["(regions)"],
322
- language: "en-US",
323
- region: "us",
324
- includedRegionCodes: ["US"],
325
- }).then(async ({ suggestions }) => {
326
- const result = suggestions[0];
327
- if (result) {
328
- if (result.placePrediction?.text.text) {
329
- setValue(result.placePrediction?.text.text);
330
- }
331
- else
332
- setValue(_value);
333
- }
334
- else
335
- setValue(_value);
336
- });
354
+ let active = true;
355
+ if (!initialValue) {
356
+ setInputValue("");
357
+ return;
358
+ }
359
+ if (row.placeId) {
360
+ const cached = getCacheByPlaceId(row.placeId);
361
+ setInputValue(cached?.[0]?.placePrediction?.text?.text || initialValue);
362
+ return;
363
+ }
364
+ getSuggestions(initialValue)
365
+ .then((suggestions) => {
366
+ if (!active)
367
+ return;
368
+ const text = suggestions?.[0]?.placePrediction?.text?.text || initialValue;
369
+ setInputValue(text);
370
+ })
371
+ .catch(() => {
372
+ if (active)
373
+ setInputValue(initialValue);
374
+ });
375
+ return () => {
376
+ active = false;
377
+ };
337
378
  }, []);
338
- const handleValueChange = async (_, newValue) => {
339
- setValue(newValue);
340
- if (typeof newValue != "string" && newValue != null) {
341
- if (newValue.placePrediction?.text.text) {
342
- const _place = newValue.placePrediction.toPlace();
343
- if (_place) {
344
- const place = (await _place.fetchFields({ fields: ["addressComponents"] })).place;
345
- const mainText = place.addressComponents?.[0]?.longText;
346
- let _value = mainText;
347
- if (Number.isNaN(Number(mainText)) &&
348
- place.addressComponents?.[2]?.shortText) {
349
- _value =
350
- place.addressComponents?.[0]?.longText +
351
- ", " +
352
- place.addressComponents?.[2]?.shortText;
353
- }
354
- apiRef.current?.setEditCellValue({
355
- id,
356
- field,
357
- value: _value,
358
- });
359
- }
360
- }
379
+ const loadSuggestions = useMemo(() => debounce(async (val) => {
380
+ if (!val)
381
+ return;
382
+ const results = await getSuggestions({
383
+ input: val,
384
+ includedPrimaryTypes: ["(regions)"],
385
+ includedRegionCodes: ["US"],
386
+ language: "en-US",
387
+ region: "us",
388
+ });
389
+ setOptions(results);
390
+ }, 300), [getSuggestions]);
391
+ const handleChange = async (_, newValue) => {
392
+ if (!newValue) {
393
+ apiRef.current.setEditCellValue({
394
+ id,
395
+ field,
396
+ value: "",
397
+ });
398
+ apiRef.current.setEditCellValue({
399
+ id,
400
+ field: "placeId",
401
+ value: undefined,
402
+ });
403
+ return;
361
404
  }
362
- else {
363
- apiRef.current?.setEditCellValue({
405
+ if (typeof newValue === "string") {
406
+ setInputValue(newValue);
407
+ apiRef.current.setEditCellValue({
364
408
  id,
365
409
  field,
366
410
  value: newValue,
367
411
  });
368
- }
369
- };
370
- const getGoogleAddresses = async (_event, value) => {
371
- if (value) {
372
- const { suggestions } = await fetchAutocompleteSuggestions({
373
- input: value,
374
- includedRegionCodes: ["US"],
375
- includedPrimaryTypes: ["(regions)"],
376
- language: "en-US",
377
- region: "us",
412
+ apiRef.current.setEditCellValue({
413
+ id,
414
+ field: "placeId",
415
+ value: undefined,
378
416
  });
379
- setGoogleMapsPredictions(suggestions);
417
+ return;
380
418
  }
419
+ const place = newValue.placePrediction?.toPlace();
420
+ if (!place)
421
+ return;
422
+ const result = await place.fetchFields({
423
+ fields: ["addressComponents"],
424
+ });
425
+ const components = result.place.addressComponents || [];
426
+ const city = components.find((c) => c.types.includes("locality"))?.longText ||
427
+ components.find((c) => c.types.includes("postal_town"))?.longText ||
428
+ components.find((c) => c.types.includes("administrative_area_level_2"))
429
+ ?.longText ||
430
+ "";
431
+ const state = components.find((c) => c.types.includes("administrative_area_level_1"))
432
+ ?.shortText || "";
433
+ const zip = components.find((c) => c.types.includes("postal_code"))?.longText || "";
434
+ const main = city || zip;
435
+ const finalValue = main && state ? `${main}, ${state}` : main || state || "";
436
+ setInputValue(finalValue);
437
+ apiRef.current.setEditCellValue({
438
+ id,
439
+ field,
440
+ value: finalValue,
441
+ });
442
+ apiRef.current.setEditCellValue({
443
+ id,
444
+ field: "placeId",
445
+ value: newValue.placePrediction?.placeId || undefined,
446
+ });
381
447
  };
382
- const debouncedChangeHandler = useMemo(() => debounce(getGoogleAddresses, 300), []);
383
- return (_jsx(Autocomplete, { fullWidth: true, sx: {
384
- "& fieldset": {
385
- borderWidth: "0px !important",
386
- },
387
- }, onClose: () => {
388
- setOpen(false);
389
- }, options: googleMapsPredictions || [], open: open, onOpen: () => setOpen(true), value: value || "", freeSolo: true, onChange: handleValueChange, onKeyDown: (e) => {
390
- if (e.key === "Enter" && open) {
391
- e.stopPropagation();
392
- setOpen(false);
393
- }
394
- }, onInputChange: (e, v) => {
395
- debouncedChangeHandler(e, v);
396
- apiRef.current?.setEditCellValue({
448
+ return (_jsx(Autocomplete, { fullWidth: true, freeSolo: true, open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), options: options, filterOptions: (x) => x, value: inputValue, getOptionLabel: (option) => typeof option === "string"
449
+ ? option
450
+ : option.placePrediction?.text.text || "", onChange: handleChange, onInputChange: (_, v) => {
451
+ if (v === inputValue)
452
+ return;
453
+ setInputValue(v);
454
+ loadSuggestions(v);
455
+ apiRef.current.setEditCellValue({
397
456
  id,
398
457
  field,
399
458
  value: v,
400
459
  });
401
- }, getOptionLabel: (option) => {
402
- if (typeof option != "string") {
403
- return option.placePrediction?.text.text || "";
404
- }
405
- else
406
- return option;
407
- }, filterOptions: (x) => x, renderInput: (params) => (_jsx(TextField, { tabIndex: 0, ...params, placeholder: "Enter zip or city, state...", autoFocus: apiRef.current?.getRowMode(id) === "edit" ? false : true, inputRef: ref, InputProps: {
408
- ...params.InputProps,
409
- tabIndex: 0,
410
- onKeyDown: (e) => {
411
- if (e.key === "Tab") {
412
- e.stopPropagation();
413
- }
414
- },
415
- } })), renderOption: (props, c) => (_jsx(LocationOption, { cellInput: true, props: props, c: c }, locationEntityTypeGuard(c) ? c.id : c.placePrediction?.placeId)) }));
460
+ }, renderInput: (params) => (_jsx(TextField, { ...params, inputRef: ref, placeholder: "Enter zip or city, state...", autoFocus: true })) }));
416
461
  };
417
462
  const NumberEditCell = ({ id, value, field }) => {
418
463
  const apiRef = useGridApiContext();
@@ -672,152 +717,125 @@ export function AccessorialInput({ accessorialIDs: _accessorialIDs, onChange, })
672
717
  * @returns {ReactElement} The autocomplete
673
718
  */
674
719
  export function LocationInputCell(props) {
675
- const { id, value, field, hasFocus, onEditLocationClick, customerPortal } = props;
720
+ const { id, field, hasFocus, onEditLocationClick, customerPortal } = props;
676
721
  const apiRef = useGridApiContext();
677
722
  const [openDialog, setOpenDialog] = useState(false);
678
723
  const [locationInputValue, setLocationInputValue] = useState("");
724
+ const [open, setOpen] = useState(false);
679
725
  const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
680
726
  const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
681
- const [bounds, setBounds] = useState();
682
727
  const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
683
728
  const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
684
729
  const googleMapsGeocoder = new google.maps.Geocoder();
685
730
  const [getLocation, { data: location }] = useLazyGetLocationQuery();
731
+ const [bounds, setBounds] = useState();
686
732
  const ref = useRef(null);
687
- const [open, setOpen] = useState(false);
733
+ const zip = useMemo(() => {
734
+ const z = props.row.zip || props.row.quoteZip || "";
735
+ return [...z.matchAll(/\d/g)].map((m) => m[0]).join("") || z;
736
+ }, [props.row.zip, props.row.quoteZip]);
688
737
  useLayoutEffect(() => {
689
- if (hasFocus) {
738
+ if (hasFocus)
690
739
  ref.current?.focus();
691
- }
692
740
  }, [hasFocus]);
693
741
  useEffect(() => {
694
- const id = Number(value);
695
- if (id) {
696
- getLocation(id, true);
697
- }
698
- }, [value]);
699
- useEffect(() => {
700
- if (props.row.zip || props.row.quoteZip) {
701
- findBounds();
702
- }
703
- }, [props.row]);
704
- const handleValueChange = async (_, newValue) => {
705
- if (typeof newValue != "string") {
706
- if (newValue && locationEntityTypeGuard(newValue)) {
707
- apiRef.current?.setEditCellValue({
708
- id,
709
- field,
710
- value: newValue,
711
- });
712
- }
713
- else if (newValue) {
714
- const place = newValue.placePrediction?.toPlace();
715
- await place?.fetchFields({
716
- fields: ["formattedAddress"],
717
- });
718
- if (place && place.formattedAddress) {
719
- try {
720
- const existingLocations = await searchExistingLocation(place.formattedAddress).unwrap();
721
- if (existingLocations.length > 0)
722
- setOpenDialog(true);
723
- else {
724
- onEditLocationClick(place, id, field);
725
- }
726
- }
727
- catch {
728
- return;
729
- }
730
- }
731
- }
732
- else {
733
- apiRef.current?.setEditCellValue({
734
- id,
735
- field,
736
- value: newValue,
737
- });
738
- }
739
- }
740
- };
741
- const findBounds = () => {
742
+ const idNum = Number(props.value);
743
+ if (idNum)
744
+ getLocation(idNum, true);
745
+ }, [props.value]);
746
+ const findBounds = useCallback(() => {
747
+ if (!zip)
748
+ return;
742
749
  googleMapsGeocoder.geocode({
743
- address: props.row.zip || props.row.quoteZip,
750
+ address: zip,
744
751
  componentRestrictions: { country: "US" },
745
752
  language: "en-US",
746
753
  }, (results, status) => {
747
- if (status === google.maps.GeocoderStatus.OK && results != undefined) {
748
- const result = results.find((r) => r.address_components.find((a) => a.types.includes("country"))
749
- ?.short_name === "US") || results[0];
750
- const lat = result.geometry.location.lat();
751
- const lng = result.geometry.location.lng();
752
- const center = new google.maps.LatLng(lat, lng);
754
+ if (status === google.maps.GeocoderStatus.OK && results?.length) {
755
+ const result = results.find((r) => r.address_components?.some((a) => a.types.includes("country"))) || results[0];
756
+ const center = new google.maps.LatLng(result.geometry.location.lat(), result.geometry.location.lng());
753
757
  const circle = new google.maps.Circle({
754
- center: center,
758
+ center,
755
759
  radius: 10000,
756
760
  });
757
761
  const bounds = circle.getBounds() || undefined;
758
762
  setBounds(bounds);
759
763
  }
760
764
  });
761
- };
762
- const getGoogleAddresses = async (searchText) => {
763
- if (searchText) {
764
- const { suggestions } = await fetchAutocompleteSuggestions({
765
- input: searchText,
766
- locationBias: bounds,
767
- language: "en-US",
768
- region: "us",
769
- includedRegionCodes: ["US"],
770
- });
771
- setGoogleMapsPredictions(suggestions);
765
+ }, [zip]);
766
+ useEffect(() => {
767
+ if (props.row.zip || props.row.quoteZip) {
768
+ findBounds();
772
769
  }
773
- };
774
- const onInputChange = useCallback((e) => {
775
- const zip = props.row.zip || props.row.quoteZip || "";
776
- const _zip = [...zip.matchAll(/\d/g)].map((m) => m[0]).join("");
770
+ }, [props.row.zip, props.row.quoteZip, findBounds]);
771
+ const getGoogleAddresses = useCallback(async (searchText) => {
772
+ if (!searchText)
773
+ return;
774
+ const { suggestions } = await fetchAutocompleteSuggestions({
775
+ input: searchText,
776
+ locationBias: bounds,
777
+ language: "en-US",
778
+ region: "us",
779
+ includedRegionCodes: ["US"],
780
+ });
781
+ setGoogleMapsPredictions(suggestions || []);
782
+ }, [bounds]);
783
+ const debouncedSearch = useMemo(() => debounce((value) => {
777
784
  searchLocation({
778
- zip: _zip ? _zip : zip,
779
- location: e.target.value,
785
+ zip,
786
+ location: value,
780
787
  });
781
- getGoogleAddresses(e.target.value);
782
- }, [props.row]);
783
- const debouncedChangeHandler = useMemo(() => debounce(onInputChange, 500), []);
788
+ getGoogleAddresses(value);
789
+ }, 400), [zip, bounds, searchLocation, getGoogleAddresses]);
790
+ const handleValueChange = async (_, newValue) => {
791
+ if (!newValue || typeof newValue === "string") {
792
+ apiRef.current?.setEditCellValue({ id, field, value: undefined });
793
+ return;
794
+ }
795
+ if (locationEntityTypeGuard(newValue)) {
796
+ apiRef.current?.setEditCellValue({
797
+ id,
798
+ field,
799
+ value: newValue,
800
+ });
801
+ return;
802
+ }
803
+ const place = newValue.placePrediction?.toPlace();
804
+ await place?.fetchFields({ fields: ["formattedAddress"] });
805
+ if (!place?.formattedAddress)
806
+ return;
807
+ try {
808
+ const existing = await searchExistingLocation(place.formattedAddress).unwrap();
809
+ if (existing.length > 0) {
810
+ setOpenDialog(true);
811
+ }
812
+ else {
813
+ onEditLocationClick(place, id, field);
814
+ }
815
+ }
816
+ catch {
817
+ return;
818
+ }
819
+ };
784
820
  return (_jsxs(_Fragment, { children: [_jsx(Autocomplete, { fullWidth: true, sx: {
785
- "& fieldset": {
786
- borderWidth: "0px !important",
787
- },
821
+ "& fieldset": { borderWidth: "0px !important" },
788
822
  }, options: [
789
823
  ...(locationSuggestions ?? []),
790
824
  ...(googleMapsPredictions ?? []),
791
- ], open: open, onClose: () => {
792
- setOpen(false);
793
- }, onOpen: () => setOpen(true), value: location || "", onChange: handleValueChange, inputValue: locationInputValue, onKeyDown: (e) => {
794
- if (e.key === "Enter" && open) {
795
- e.stopPropagation();
796
- setOpen(false);
797
- }
798
- }, freeSolo: true, onInputChange: (_e, v) => setLocationInputValue(v), getOptionLabel: (option) => {
799
- if (typeof option != "string") {
800
- if (locationEntityTypeGuard(option)) {
801
- return option.name;
802
- }
803
- return option.placePrediction?.mainText?.text || "";
804
- }
805
- else
806
- return option;
807
- }, filterOptions: (x) => x, renderInput: (params) => (_jsx(TextField, { tabIndex: 0, variant: "outlined", ...params, size: "small", placeholder: "Pick a location...", autoFocus: apiRef.current?.getRowMode(id) === "edit" ? false : true, onChange: (e) => {
808
- debouncedChangeHandler(e);
809
- }, inputRef: ref, InputProps: {
810
- ...params.InputProps,
811
- tabIndex: 0,
812
- } })), renderOption: (props, c) => (_jsx(LocationOption, { cellInput: true, props: props, c: c }, locationEntityTypeGuard(c) ? c.id : c.placePrediction?.placeId)) }), _jsx(ExistingLocations, { updateCell: (value) => {
825
+ ], open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), value: location || "", inputValue: locationInputValue, freeSolo: true, filterOptions: (x) => x, getOptionLabel: (option) => typeof option === "string"
826
+ ? option
827
+ : locationEntityTypeGuard(option)
828
+ ? option.name
829
+ : option.placePrediction?.mainText?.text || "", onChange: handleValueChange, onInputChange: (_e, v) => {
830
+ setLocationInputValue(v);
831
+ debouncedSearch(v);
832
+ }, renderInput: (params) => (_jsx(TextField, { ...params, size: "small", placeholder: "Pick a location...", inputRef: ref })), renderOption: (props, c) => (_jsx(LocationOption, { cellInput: true, props: props, c: c }, locationEntityTypeGuard(c) ? c.id : c.placePrediction?.placeId)) }), _jsx(ExistingLocations, { updateCell: (value) => {
813
833
  apiRef.current?.setEditCellValue({
814
834
  id,
815
835
  field,
816
- value: value,
836
+ value,
817
837
  });
818
- }, existingLocations: existingLocations, close: () => {
819
- setOpenDialog(false);
820
- }, open: openDialog, openLocationForm: (place) => onEditLocationClick(place, id, field), customerPortal: customerPortal })] }));
838
+ }, existingLocations: existingLocations, close: () => setOpenDialog(false), open: openDialog, openLocationForm: (place) => onEditLocationClick(place, id, field), customerPortal: customerPortal })] }));
821
839
  }
822
840
  export const LocationInput = ({ zip, locationID, onChange, customerPortal, autoCompleteProps, }) => {
823
841
  const [locationFormOpen, setLocationFormOpen] = useState({
@@ -825,131 +843,92 @@ export const LocationInput = ({ zip, locationID, onChange, customerPortal, autoC
825
843
  place: undefined,
826
844
  });
827
845
  const [openDialog, setOpenDialog] = useState(false);
828
- const [locationInputValue, setLocationInputValue] = useState("");
829
- const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
830
- const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
831
- const [bounds, setBounds] = useState();
846
+ const [inputValue, setInputValue] = useState("");
832
847
  const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
833
- const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
834
848
  const googleMapsGeocoder = new google.maps.Geocoder();
849
+ const [bounds, setBounds] = useState();
850
+ const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
851
+ const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
835
852
  const [getLocation] = useLazyGetLocationQuery();
836
853
  const [location, setLocation] = useState();
854
+ const { getSuggestions } = useGoogleMapsAutocompleteCache();
837
855
  useEffect(() => {
838
856
  if (locationID) {
839
857
  getLocation(locationID, true)
840
858
  .unwrap()
841
- .then((d) => setLocation(d))
859
+ .then(setLocation)
842
860
  .catch(() => undefined);
843
861
  }
844
- else
862
+ else {
845
863
  setLocation(undefined);
864
+ }
846
865
  }, [locationID]);
847
866
  useEffect(() => {
848
- if (zip) {
849
- findBounds();
850
- }
851
- }, [zip]);
852
- const handleValueChange = async (_, newValue) => {
853
- if (typeof newValue != "string")
854
- if (newValue && locationEntityTypeGuard(newValue)) {
855
- onChange(newValue);
856
- }
857
- else if (newValue) {
858
- const place = newValue.placePrediction?.toPlace();
859
- await place?.fetchFields({
860
- fields: ["formattedAddress"],
861
- });
862
- if (place && place.formattedAddress) {
863
- try {
864
- const existingLocations = await searchExistingLocation(place?.formattedAddress).unwrap();
865
- if (existingLocations.length > 0)
866
- setOpenDialog(true);
867
- else {
868
- setLocationFormOpen({ open: true, place: place });
869
- }
870
- }
871
- catch {
872
- return;
873
- }
874
- }
875
- }
876
- else {
877
- onChange(newValue);
878
- }
879
- };
880
- const findBounds = () => {
867
+ if (!zip)
868
+ return;
881
869
  googleMapsGeocoder.geocode({
882
870
  address: zip,
883
871
  componentRestrictions: { country: "US" },
884
- language: "en",
885
872
  }, (results, status) => {
886
- if (status === google.maps.GeocoderStatus.OK && results != undefined) {
887
- const result = results[0];
888
- const lat = result.geometry.location.lat();
889
- const lng = result.geometry.location.lng();
890
- const center = new google.maps.LatLng(lat, lng);
873
+ if (status === google.maps.GeocoderStatus.OK && results?.[0]) {
874
+ const loc = results[0].geometry.location;
891
875
  const circle = new google.maps.Circle({
892
- center: center,
876
+ center: loc,
893
877
  radius: 10000,
894
878
  });
895
- const bounds = circle.getBounds() || undefined;
896
- setBounds(bounds);
879
+ setBounds(circle.getBounds() || undefined);
897
880
  }
898
881
  });
899
- };
900
- const getGoogleAddresses = async (searchText) => {
901
- if (searchText) {
902
- const { suggestions } = await fetchAutocompleteSuggestions({
903
- input: searchText,
904
- locationBias: bounds,
905
- language: "en-US",
906
- region: "us",
907
- includedRegionCodes: ["US"],
882
+ }, [zip]);
883
+ const handleValueChange = async (_, newValue) => {
884
+ if (typeof newValue !== "string") {
885
+ if (!newValue) {
886
+ onChange(undefined);
887
+ return;
888
+ }
889
+ if (locationEntityTypeGuard(newValue)) {
890
+ onChange(newValue);
891
+ return;
892
+ }
893
+ const place = newValue.placePrediction?.toPlace();
894
+ if (!place)
895
+ return;
896
+ const result = await place.fetchFields({
897
+ fields: ["formattedAddress"],
908
898
  });
909
- setGoogleMapsPredictions(suggestions);
899
+ const formatted = result.place.formattedAddress;
900
+ if (!formatted)
901
+ return;
902
+ const existing = await searchExistingLocation(formatted).unwrap();
903
+ if (existing.length > 0) {
904
+ setOpenDialog(true);
905
+ return;
906
+ }
907
+ setLocationFormOpen({ open: true, place });
910
908
  }
911
909
  };
912
- const onInputChange = useCallback((v) => {
913
- searchLocation({ zip: zip, location: v });
914
- getGoogleAddresses(v);
915
- }, [zip]);
916
- const debouncedChangeHandler = useMemo(() => debounce(onInputChange, 500), []);
917
- return (_jsxs(_Fragment, { children: [_jsxs(FormControl, { children: [_jsx(FormLabel, { children: "Pick a location..." }), _jsx(JoyAutocomplete, { size: "sm", ...autoCompleteProps, slotProps: {
918
- listbox: {
919
- sx: {
920
- zIndex: (theme) => theme.zIndex.modal,
921
- },
922
- },
923
- }, options: [
924
- ...(locationSuggestions ?? []),
925
- ...(googleMapsPredictions ?? []),
926
- ], filterOptions: (x) => x, value: location || "", onChange: handleValueChange, inputValue: locationInputValue, freeSolo: true, onInputChange: (_e, v) => {
927
- setLocationInputValue(v);
928
- debouncedChangeHandler(v);
929
- }, getOptionLabel: (option) => {
930
- if (typeof option != "string") {
931
- if (locationEntityTypeGuard(option)) {
932
- return option.name;
933
- }
934
- return option.placePrediction?.mainText?.text || "";
935
- }
936
- else
937
- return option;
938
- }, endDecorator: _jsx(IconButton, { sx: {
939
- display: location ? "inherit" : "none",
940
- }, size: "small", onClick: () => {
941
- setLocationFormOpen({ open: true, place: location });
942
- }, children: _jsx("i", { className: "fa-solid fa-pen-to-square" }) }), renderOption: (props, c) => typeof c !== "string" ? (_jsx(LocationOption, { props: props, c: c, cellInput: false }, locationEntityTypeGuard(c) ? c.id : c.placePrediction?.placeId)) : (c) })] }), customerPortal ? (_jsx(LocationForm, { open: locationFormOpen.open, onClose: () => setLocationFormOpen({ open: false, place: undefined }), place: locationFormOpen.place, onConfirm: (value) => {
943
- onChange(value);
944
- } })) : (_jsx(LocationFormSales, { onClose: () => setLocationFormOpen({ open: false, place: undefined }), open: locationFormOpen.open, place: locationFormOpen.place, onCreateLocation: (value) => {
945
- onChange(value);
946
- }, onSaveLocation: (value) => {
947
- onChange(value);
948
- } })), _jsx(ExistingLocations, { updateCell: (value) => {
949
- onChange(value);
950
- }, existingLocations: existingLocations, close: () => {
951
- setOpenDialog(false);
952
- }, open: openDialog, openLocationForm: (place, open) => setLocationFormOpen({ open, place }), customerPortal: customerPortal })] }));
910
+ const debouncedSearch = useMemo(() => debounce(async (v) => {
911
+ searchLocation({ zip, location: v });
912
+ const googleResults = await getSuggestions({
913
+ input: v,
914
+ locationBias: bounds,
915
+ includedRegionCodes: ["US"],
916
+ language: "en-US",
917
+ region: "us",
918
+ });
919
+ setGoogleMapsPredictions(googleResults);
920
+ }, 400), [zip, bounds]);
921
+ return (_jsxs(_Fragment, { children: [_jsxs(FormControl, { children: [_jsx(FormLabel, { children: "Pick a location..." }), _jsx(JoyAutocomplete, { ...autoCompleteProps, size: "sm", value: location || null, inputValue: inputValue, freeSolo: true, options: [...(locationSuggestions ?? []), ...googleMapsPredictions], filterOptions: (x) => x, getOptionLabel: (option) => typeof option === "string"
922
+ ? option
923
+ : locationEntityTypeGuard(option)
924
+ ? option.name
925
+ : option.placePrediction?.mainText?.text || "", onChange: handleValueChange, onInputChange: (_, v) => {
926
+ setInputValue(v);
927
+ debouncedSearch(v);
928
+ }, endDecorator: location ? (_jsx(IconButton, { size: "small", onClick: () => setLocationFormOpen({
929
+ open: true,
930
+ place: location,
931
+ }), children: _jsx("i", { className: "fa-solid fa-pen-to-square" }) })) : null, renderOption: (props, c) => typeof c !== "string" ? (_jsx(LocationOption, { props: props, c: c, cellInput: false }, locationEntityTypeGuard(c) ? c.id : c.placePrediction?.placeId)) : (c) })] }), customerPortal ? (_jsx(LocationForm, { open: locationFormOpen.open, onClose: () => setLocationFormOpen({ open: false, place: undefined }), place: locationFormOpen.place, onConfirm: onChange })) : (_jsx(LocationFormSales, { open: locationFormOpen.open, onClose: () => setLocationFormOpen({ open: false, place: undefined }), place: locationFormOpen.place, onCreateLocation: onChange, onSaveLocation: onChange })), _jsx(ExistingLocations, { open: openDialog, close: () => setOpenDialog(false), existingLocations: existingLocations, updateCell: onChange, openLocationForm: (place, open) => setLocationFormOpen({ open, place }), customerPortal: customerPortal })] }));
953
932
  };
954
933
  /**
955
934
  *