@nerdjs/sales-kit 4.0.56 → 4.0.58
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.
- package/dist/entities/quote/quote.d.ts +2 -1
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.js +54 -0
- package/dist/helpers.js.map +1 -1
- package/dist/hooks/lep/lepLoadLocation.d.ts +0 -6
- package/dist/hooks/lep/lepLoadLocation.js +207 -177
- package/dist/hooks/lep/lepLoadLocation.js.map +1 -1
- package/dist/hooks/lep/lepShipcons.js +25 -7
- package/dist/hooks/lep/lepShipcons.js.map +1 -1
- package/dist/hooks/lep/nerdMap/nerdMap.js +28 -40
- package/dist/hooks/lep/nerdMap/nerdMap.js.map +1 -1
- package/dist/hooks/locationsForm/locationFormSales/locationFormSales.d.ts +1 -1
- package/dist/hooks/locationsForm/locationFormSales/locationFormSales.js +4 -3
- package/dist/hooks/locationsForm/locationFormSales/locationFormSales.js.map +1 -1
- package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.d.ts +0 -5
- package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.js +173 -56
- package/dist/hooks/locationsForm/locationFormSales/locationFormSalesMain.js.map +1 -1
- package/dist/hooks/quote/constants.d.ts +1 -1
- package/dist/hooks/quote/constants.js +2 -2
- package/dist/hooks/quote/constants.js.map +1 -1
- package/dist/hooks/quote/form.js +16 -5
- package/dist/hooks/quote/form.js.map +1 -1
- package/dist/hooks/quote/helpers.d.ts +1 -0
- package/dist/hooks/quote/helpers.js +263 -327
- package/dist/hooks/quote/helpers.js.map +1 -1
- package/dist/hooks/quote/template.js +2 -2
- package/dist/hooks/quote/template.js.map +1 -1
- package/dist/redux/commoditiesTasksSlice/commoditiesTasksSelectors.d.ts +2 -2
- package/dist/redux/customerV1/customerV1Endpoints.d.ts +2 -2
- package/dist/redux/lep/lepSelectors.d.ts +6 -6
- package/dist/redux/load/loadDrawerSlice.d.ts +1 -1
- package/dist/redux/loadInfoTasksSlice/loadInfoTasksSelectors.d.ts +10 -10
- package/dist/redux/locationV1/locationV1Endpoints.d.ts +12 -12
- package/dist/redux/quote/quoteAction.d.ts +2 -3
- package/dist/redux/quote/quoteAction.js +2 -3
- package/dist/redux/quote/quoteAction.js.map +1 -1
- package/dist/redux/quote/quoteCustomerPortalEndpoints.d.ts +1 -1
- package/dist/redux/quote/quoteReducer.js +5 -5
- package/dist/redux/quote/quoteReducer.js.map +1 -1
- package/dist/redux/quote/quoteSelectors.d.ts +23 -23
- package/dist/redux/shipconsTasksSlice/shipconsTasksSelectors.d.ts +2 -2
- package/dist/redux/tender/tenderSelector.d.ts +7 -7
- 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 { 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.
|
|
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
|
-
|
|
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
|
-
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
93
98
|
return {
|
|
94
99
|
...row,
|
|
95
100
|
locationId: value,
|
|
101
|
+
placeId: undefined,
|
|
96
102
|
};
|
|
97
|
-
|
|
98
|
-
|
|
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.
|
|
131
|
+
dispatch(removeStop(p.row.id));
|
|
124
132
|
}, color: "inherit" }, "delete"),
|
|
125
133
|
],
|
|
126
134
|
},
|
|
@@ -275,144 +283,138 @@ export const getPalletsColumnDef = (deleteAction, customerPortal, perPalletClass
|
|
|
275
283
|
});
|
|
276
284
|
return ret;
|
|
277
285
|
};
|
|
278
|
-
const ZipCodeRenderCell = ({ value, errors, }) => {
|
|
279
|
-
const
|
|
280
|
-
const
|
|
286
|
+
const ZipCodeRenderCell = ({ value, errors, row, id, }) => {
|
|
287
|
+
const apiRef = useGridApiContext();
|
|
288
|
+
const { getSuggestions } = useGoogleMapsAutocompleteCache();
|
|
281
289
|
useEffect(() => {
|
|
282
|
-
|
|
283
|
-
|
|
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((
|
|
290
|
-
|
|
301
|
+
}).then((suggestions) => {
|
|
302
|
+
if (!active || !suggestions?.[0])
|
|
303
|
+
return;
|
|
304
|
+
// Only enrich the row with a placeId — never overwrite the user's
|
|
305
|
+
// entered value (zip stays zip, "City, ST" stays "City, ST").
|
|
306
|
+
apiRef.current.updateRows([
|
|
307
|
+
{
|
|
308
|
+
id,
|
|
309
|
+
placeId: suggestions[0].placePrediction?.placeId || undefined,
|
|
310
|
+
},
|
|
311
|
+
]);
|
|
291
312
|
});
|
|
292
|
-
|
|
313
|
+
return () => {
|
|
314
|
+
active = false;
|
|
315
|
+
};
|
|
293
316
|
}, [value]);
|
|
294
317
|
return (_jsx(Box, { width: "100%", children: value ? (_jsx(Typography, { level: "body-sm", sx: {
|
|
295
318
|
display: "inline",
|
|
296
|
-
opacity:
|
|
297
|
-
color: (theme) => errors
|
|
298
|
-
}, children:
|
|
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: "*" })] })) }));
|
|
319
|
+
opacity: errors ? 1 : 0.5,
|
|
320
|
+
color: (theme) => (errors ? theme.palette.danger[500] : undefined),
|
|
321
|
+
}, 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
322
|
};
|
|
305
|
-
const ZipCodeRenderEditCell = ({
|
|
323
|
+
export const ZipCodeRenderEditCell = ({ id, field, hasFocus, value: initialValue, }) => {
|
|
306
324
|
const apiRef = useGridApiContext();
|
|
307
|
-
const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
|
|
308
|
-
const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
|
|
309
325
|
const ref = useRef(null);
|
|
326
|
+
const { getSuggestions } = useGoogleMapsAutocompleteCache();
|
|
327
|
+
const [options, setOptions] = useState([]);
|
|
328
|
+
const [inputValue, setInputValue] = useState("");
|
|
310
329
|
const [open, setOpen] = useState(false);
|
|
311
|
-
const [value, setValue] = useState(null);
|
|
312
330
|
useLayoutEffect(() => {
|
|
313
|
-
if (hasFocus)
|
|
331
|
+
if (hasFocus)
|
|
314
332
|
ref.current?.focus();
|
|
315
|
-
}
|
|
316
333
|
}, [hasFocus]);
|
|
317
334
|
useEffect(() => {
|
|
318
|
-
|
|
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
|
-
});
|
|
335
|
+
setInputValue(initialValue || "");
|
|
337
336
|
}, []);
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
apiRef.current?.setEditCellValue({
|
|
355
|
-
id,
|
|
356
|
-
field,
|
|
357
|
-
value: _value,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
apiRef.current?.setEditCellValue({
|
|
337
|
+
const loadSuggestions = useMemo(() => debounce(async (val) => {
|
|
338
|
+
if (!val)
|
|
339
|
+
return;
|
|
340
|
+
const results = await getSuggestions({
|
|
341
|
+
input: val,
|
|
342
|
+
includedPrimaryTypes: ["(regions)"],
|
|
343
|
+
includedRegionCodes: ["US"],
|
|
344
|
+
language: "en-US",
|
|
345
|
+
region: "us",
|
|
346
|
+
});
|
|
347
|
+
setOptions(results);
|
|
348
|
+
}, 300), [getSuggestions]);
|
|
349
|
+
const handleChange = async (_, newValue) => {
|
|
350
|
+
if (!newValue) {
|
|
351
|
+
apiRef.current.setEditCellValue({ id, field, value: "" });
|
|
352
|
+
apiRef.current.setEditCellValue({
|
|
364
353
|
id,
|
|
365
|
-
field,
|
|
366
|
-
value:
|
|
354
|
+
field: "placeId",
|
|
355
|
+
value: undefined,
|
|
367
356
|
});
|
|
357
|
+
return;
|
|
368
358
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
language: "en-US",
|
|
377
|
-
region: "us",
|
|
359
|
+
if (typeof newValue === "string") {
|
|
360
|
+
setInputValue(newValue);
|
|
361
|
+
apiRef.current.setEditCellValue({ id, field, value: newValue });
|
|
362
|
+
apiRef.current.setEditCellValue({
|
|
363
|
+
id,
|
|
364
|
+
field: "placeId",
|
|
365
|
+
value: undefined,
|
|
378
366
|
});
|
|
379
|
-
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// Capture intent BEFORE the async boundary — onInputChange("reset") fires
|
|
370
|
+
// synchronously after onChange and would overwrite inputValue state, but
|
|
371
|
+
// the closure here still holds the value the user actually typed.
|
|
372
|
+
const isZipInput = /^\d+$/.test(inputValue.trim());
|
|
373
|
+
const place = newValue.placePrediction?.toPlace();
|
|
374
|
+
if (!place)
|
|
375
|
+
return;
|
|
376
|
+
const result = await place.fetchFields({ fields: ["addressComponents"] });
|
|
377
|
+
const components = result.place.addressComponents ?? [];
|
|
378
|
+
let finalValue;
|
|
379
|
+
if (isZipInput) {
|
|
380
|
+
// User typed digits → fill with the zip code from the selected place
|
|
381
|
+
finalValue =
|
|
382
|
+
components.find((c) => c.types.includes("postal_code"))?.longText ||
|
|
383
|
+
inputValue;
|
|
380
384
|
}
|
|
385
|
+
else {
|
|
386
|
+
// User typed city/state text → fill with "City, ST"
|
|
387
|
+
const city = components.find((c) => c.types.includes("locality"))?.longText ||
|
|
388
|
+
components.find((c) => c.types.includes("postal_town"))?.longText ||
|
|
389
|
+
components.find((c) => c.types.includes("administrative_area_level_2"))
|
|
390
|
+
?.longText ||
|
|
391
|
+
"";
|
|
392
|
+
const state = components.find((c) => c.types.includes("administrative_area_level_1"))
|
|
393
|
+
?.shortText || "";
|
|
394
|
+
finalValue =
|
|
395
|
+
city && state ? `${city}, ${state}` : city || state || inputValue;
|
|
396
|
+
}
|
|
397
|
+
setInputValue(finalValue);
|
|
398
|
+
apiRef.current.setEditCellValue({ id, field, value: finalValue });
|
|
399
|
+
apiRef.current.setEditCellValue({
|
|
400
|
+
id,
|
|
401
|
+
field: "placeId",
|
|
402
|
+
value: newValue.placePrediction?.placeId || undefined,
|
|
403
|
+
});
|
|
381
404
|
};
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
"
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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({
|
|
405
|
+
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"
|
|
406
|
+
? option
|
|
407
|
+
: option.placePrediction?.text.text || "", onChange: handleChange, onInputChange: (_, v) => {
|
|
408
|
+
if (v === inputValue)
|
|
409
|
+
return;
|
|
410
|
+
setInputValue(v);
|
|
411
|
+
loadSuggestions(v);
|
|
412
|
+
apiRef.current.setEditCellValue({
|
|
397
413
|
id,
|
|
398
414
|
field,
|
|
399
415
|
value: v,
|
|
400
416
|
});
|
|
401
|
-
},
|
|
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)) }));
|
|
417
|
+
}, renderInput: (params) => (_jsx(TextField, { ...params, inputRef: ref, placeholder: "Enter zip or city, state...", autoFocus: true })) }));
|
|
416
418
|
};
|
|
417
419
|
const NumberEditCell = ({ id, value, field }) => {
|
|
418
420
|
const apiRef = useGridApiContext();
|
|
@@ -672,152 +674,125 @@ export function AccessorialInput({ accessorialIDs: _accessorialIDs, onChange, })
|
|
|
672
674
|
* @returns {ReactElement} The autocomplete
|
|
673
675
|
*/
|
|
674
676
|
export function LocationInputCell(props) {
|
|
675
|
-
const { id,
|
|
677
|
+
const { id, field, hasFocus, onEditLocationClick, customerPortal } = props;
|
|
676
678
|
const apiRef = useGridApiContext();
|
|
677
679
|
const [openDialog, setOpenDialog] = useState(false);
|
|
678
680
|
const [locationInputValue, setLocationInputValue] = useState("");
|
|
681
|
+
const [open, setOpen] = useState(false);
|
|
679
682
|
const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
|
|
680
683
|
const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
|
|
681
|
-
const [bounds, setBounds] = useState();
|
|
682
684
|
const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
|
|
683
685
|
const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
|
|
684
686
|
const googleMapsGeocoder = new google.maps.Geocoder();
|
|
685
687
|
const [getLocation, { data: location }] = useLazyGetLocationQuery();
|
|
688
|
+
const [bounds, setBounds] = useState();
|
|
686
689
|
const ref = useRef(null);
|
|
687
|
-
const
|
|
690
|
+
const zip = useMemo(() => {
|
|
691
|
+
const z = props.row.zip || props.row.quoteZip || "";
|
|
692
|
+
return [...z.matchAll(/\d/g)].map((m) => m[0]).join("") || z;
|
|
693
|
+
}, [props.row.zip, props.row.quoteZip]);
|
|
688
694
|
useLayoutEffect(() => {
|
|
689
|
-
if (hasFocus)
|
|
695
|
+
if (hasFocus)
|
|
690
696
|
ref.current?.focus();
|
|
691
|
-
}
|
|
692
697
|
}, [hasFocus]);
|
|
693
698
|
useEffect(() => {
|
|
694
|
-
const
|
|
695
|
-
if (
|
|
696
|
-
getLocation(
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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 = () => {
|
|
699
|
+
const idNum = Number(props.value);
|
|
700
|
+
if (idNum)
|
|
701
|
+
getLocation(idNum, true);
|
|
702
|
+
}, [props.value]);
|
|
703
|
+
const findBounds = useCallback(() => {
|
|
704
|
+
if (!zip)
|
|
705
|
+
return;
|
|
742
706
|
googleMapsGeocoder.geocode({
|
|
743
|
-
address:
|
|
707
|
+
address: zip,
|
|
744
708
|
componentRestrictions: { country: "US" },
|
|
745
709
|
language: "en-US",
|
|
746
710
|
}, (results, status) => {
|
|
747
|
-
if (status === google.maps.GeocoderStatus.OK && results
|
|
748
|
-
const result = results.find((r) => r.address_components
|
|
749
|
-
|
|
750
|
-
const lat = result.geometry.location.lat();
|
|
751
|
-
const lng = result.geometry.location.lng();
|
|
752
|
-
const center = new google.maps.LatLng(lat, lng);
|
|
711
|
+
if (status === google.maps.GeocoderStatus.OK && results?.length) {
|
|
712
|
+
const result = results.find((r) => r.address_components?.some((a) => a.types.includes("country"))) || results[0];
|
|
713
|
+
const center = new google.maps.LatLng(result.geometry.location.lat(), result.geometry.location.lng());
|
|
753
714
|
const circle = new google.maps.Circle({
|
|
754
|
-
center
|
|
715
|
+
center,
|
|
755
716
|
radius: 10000,
|
|
756
717
|
});
|
|
757
718
|
const bounds = circle.getBounds() || undefined;
|
|
758
719
|
setBounds(bounds);
|
|
759
720
|
}
|
|
760
721
|
});
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
if (
|
|
764
|
-
|
|
765
|
-
input: searchText,
|
|
766
|
-
locationBias: bounds,
|
|
767
|
-
language: "en-US",
|
|
768
|
-
region: "us",
|
|
769
|
-
includedRegionCodes: ["US"],
|
|
770
|
-
});
|
|
771
|
-
setGoogleMapsPredictions(suggestions);
|
|
722
|
+
}, [zip]);
|
|
723
|
+
useEffect(() => {
|
|
724
|
+
if (props.row.zip || props.row.quoteZip) {
|
|
725
|
+
findBounds();
|
|
772
726
|
}
|
|
773
|
-
};
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
727
|
+
}, [props.row.zip, props.row.quoteZip, findBounds]);
|
|
728
|
+
const getGoogleAddresses = useCallback(async (searchText) => {
|
|
729
|
+
if (!searchText)
|
|
730
|
+
return;
|
|
731
|
+
const { suggestions } = await fetchAutocompleteSuggestions({
|
|
732
|
+
input: searchText,
|
|
733
|
+
locationBias: bounds,
|
|
734
|
+
language: "en-US",
|
|
735
|
+
region: "us",
|
|
736
|
+
includedRegionCodes: ["US"],
|
|
737
|
+
});
|
|
738
|
+
setGoogleMapsPredictions(suggestions || []);
|
|
739
|
+
}, [bounds]);
|
|
740
|
+
const debouncedSearch = useMemo(() => debounce((value) => {
|
|
777
741
|
searchLocation({
|
|
778
|
-
zip
|
|
779
|
-
location:
|
|
742
|
+
zip,
|
|
743
|
+
location: value,
|
|
780
744
|
});
|
|
781
|
-
getGoogleAddresses(
|
|
782
|
-
}, [
|
|
783
|
-
const
|
|
745
|
+
getGoogleAddresses(value);
|
|
746
|
+
}, 400), [zip, bounds, searchLocation, getGoogleAddresses]);
|
|
747
|
+
const handleValueChange = async (_, newValue) => {
|
|
748
|
+
if (!newValue || typeof newValue === "string") {
|
|
749
|
+
apiRef.current?.setEditCellValue({ id, field, value: undefined });
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (locationEntityTypeGuard(newValue)) {
|
|
753
|
+
apiRef.current?.setEditCellValue({
|
|
754
|
+
id,
|
|
755
|
+
field,
|
|
756
|
+
value: newValue,
|
|
757
|
+
});
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const place = newValue.placePrediction?.toPlace();
|
|
761
|
+
await place?.fetchFields({ fields: ["formattedAddress"] });
|
|
762
|
+
if (!place?.formattedAddress)
|
|
763
|
+
return;
|
|
764
|
+
try {
|
|
765
|
+
const existing = await searchExistingLocation(place.formattedAddress).unwrap();
|
|
766
|
+
if (existing.length > 0) {
|
|
767
|
+
setOpenDialog(true);
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
onEditLocationClick(place, id, field);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
catch {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
};
|
|
784
777
|
return (_jsxs(_Fragment, { children: [_jsx(Autocomplete, { fullWidth: true, sx: {
|
|
785
|
-
"& fieldset": {
|
|
786
|
-
borderWidth: "0px !important",
|
|
787
|
-
},
|
|
778
|
+
"& fieldset": { borderWidth: "0px !important" },
|
|
788
779
|
}, options: [
|
|
789
780
|
...(locationSuggestions ?? []),
|
|
790
781
|
...(googleMapsPredictions ?? []),
|
|
791
|
-
], open: open, onClose: () =>
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
},
|
|
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) => {
|
|
782
|
+
], open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), value: location || "", inputValue: locationInputValue, freeSolo: true, filterOptions: (x) => x, getOptionLabel: (option) => typeof option === "string"
|
|
783
|
+
? option
|
|
784
|
+
: locationEntityTypeGuard(option)
|
|
785
|
+
? option.name
|
|
786
|
+
: option.placePrediction?.mainText?.text || "", onChange: handleValueChange, onInputChange: (_e, v) => {
|
|
787
|
+
setLocationInputValue(v);
|
|
788
|
+
debouncedSearch(v);
|
|
789
|
+
}, 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
790
|
apiRef.current?.setEditCellValue({
|
|
814
791
|
id,
|
|
815
792
|
field,
|
|
816
|
-
value
|
|
793
|
+
value,
|
|
817
794
|
});
|
|
818
|
-
}, existingLocations: existingLocations, close: () =>
|
|
819
|
-
setOpenDialog(false);
|
|
820
|
-
}, open: openDialog, openLocationForm: (place) => onEditLocationClick(place, id, field), customerPortal: customerPortal })] }));
|
|
795
|
+
}, existingLocations: existingLocations, close: () => setOpenDialog(false), open: openDialog, openLocationForm: (place) => onEditLocationClick(place, id, field), customerPortal: customerPortal })] }));
|
|
821
796
|
}
|
|
822
797
|
export const LocationInput = ({ zip, locationID, onChange, customerPortal, autoCompleteProps, }) => {
|
|
823
798
|
const [locationFormOpen, setLocationFormOpen] = useState({
|
|
@@ -825,131 +800,92 @@ export const LocationInput = ({ zip, locationID, onChange, customerPortal, autoC
|
|
|
825
800
|
place: undefined,
|
|
826
801
|
});
|
|
827
802
|
const [openDialog, setOpenDialog] = useState(false);
|
|
828
|
-
const [
|
|
829
|
-
const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
|
|
830
|
-
const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
|
|
831
|
-
const [bounds, setBounds] = useState();
|
|
803
|
+
const [inputValue, setInputValue] = useState("");
|
|
832
804
|
const [googleMapsPredictions, setGoogleMapsPredictions] = useState([]);
|
|
833
|
-
const fetchAutocompleteSuggestions = google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions;
|
|
834
805
|
const googleMapsGeocoder = new google.maps.Geocoder();
|
|
806
|
+
const [bounds, setBounds] = useState();
|
|
807
|
+
const [searchExistingLocation, { data: existingLocations }] = useLazySearchExistingLocationQuery();
|
|
808
|
+
const [searchLocation, { data: locationSuggestions }] = useLazySearchLocationQuery();
|
|
835
809
|
const [getLocation] = useLazyGetLocationQuery();
|
|
836
810
|
const [location, setLocation] = useState();
|
|
811
|
+
const { getSuggestions } = useGoogleMapsAutocompleteCache();
|
|
837
812
|
useEffect(() => {
|
|
838
813
|
if (locationID) {
|
|
839
814
|
getLocation(locationID, true)
|
|
840
815
|
.unwrap()
|
|
841
|
-
.then(
|
|
816
|
+
.then(setLocation)
|
|
842
817
|
.catch(() => undefined);
|
|
843
818
|
}
|
|
844
|
-
else
|
|
819
|
+
else {
|
|
845
820
|
setLocation(undefined);
|
|
821
|
+
}
|
|
846
822
|
}, [locationID]);
|
|
847
823
|
useEffect(() => {
|
|
848
|
-
if (zip)
|
|
849
|
-
|
|
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 = () => {
|
|
824
|
+
if (!zip)
|
|
825
|
+
return;
|
|
881
826
|
googleMapsGeocoder.geocode({
|
|
882
827
|
address: zip,
|
|
883
828
|
componentRestrictions: { country: "US" },
|
|
884
|
-
language: "en",
|
|
885
829
|
}, (results, status) => {
|
|
886
|
-
if (status === google.maps.GeocoderStatus.OK && results
|
|
887
|
-
const
|
|
888
|
-
const lat = result.geometry.location.lat();
|
|
889
|
-
const lng = result.geometry.location.lng();
|
|
890
|
-
const center = new google.maps.LatLng(lat, lng);
|
|
830
|
+
if (status === google.maps.GeocoderStatus.OK && results?.[0]) {
|
|
831
|
+
const loc = results[0].geometry.location;
|
|
891
832
|
const circle = new google.maps.Circle({
|
|
892
|
-
center:
|
|
833
|
+
center: loc,
|
|
893
834
|
radius: 10000,
|
|
894
835
|
});
|
|
895
|
-
|
|
896
|
-
setBounds(bounds);
|
|
836
|
+
setBounds(circle.getBounds() || undefined);
|
|
897
837
|
}
|
|
898
838
|
});
|
|
899
|
-
};
|
|
900
|
-
const
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
839
|
+
}, [zip]);
|
|
840
|
+
const handleValueChange = async (_, newValue) => {
|
|
841
|
+
if (typeof newValue !== "string") {
|
|
842
|
+
if (!newValue) {
|
|
843
|
+
onChange(undefined);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (locationEntityTypeGuard(newValue)) {
|
|
847
|
+
onChange(newValue);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
const place = newValue.placePrediction?.toPlace();
|
|
851
|
+
if (!place)
|
|
852
|
+
return;
|
|
853
|
+
const result = await place.fetchFields({
|
|
854
|
+
fields: ["formattedAddress"],
|
|
908
855
|
});
|
|
909
|
-
|
|
856
|
+
const formatted = result.place.formattedAddress;
|
|
857
|
+
if (!formatted)
|
|
858
|
+
return;
|
|
859
|
+
const existing = await searchExistingLocation(formatted).unwrap();
|
|
860
|
+
if (existing.length > 0) {
|
|
861
|
+
setOpenDialog(true);
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
setLocationFormOpen({ open: true, place });
|
|
910
865
|
}
|
|
911
866
|
};
|
|
912
|
-
const
|
|
913
|
-
searchLocation({ zip
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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 })] }));
|
|
867
|
+
const debouncedSearch = useMemo(() => debounce(async (v) => {
|
|
868
|
+
searchLocation({ zip, location: v });
|
|
869
|
+
const googleResults = await getSuggestions({
|
|
870
|
+
input: v,
|
|
871
|
+
locationBias: bounds,
|
|
872
|
+
includedRegionCodes: ["US"],
|
|
873
|
+
language: "en-US",
|
|
874
|
+
region: "us",
|
|
875
|
+
});
|
|
876
|
+
setGoogleMapsPredictions(googleResults);
|
|
877
|
+
}, 400), [zip, bounds]);
|
|
878
|
+
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"
|
|
879
|
+
? option
|
|
880
|
+
: locationEntityTypeGuard(option)
|
|
881
|
+
? option.name
|
|
882
|
+
: option.placePrediction?.mainText?.text || "", onChange: handleValueChange, onInputChange: (_, v) => {
|
|
883
|
+
setInputValue(v);
|
|
884
|
+
debouncedSearch(v);
|
|
885
|
+
}, endDecorator: location ? (_jsx(IconButton, { size: "small", onClick: () => setLocationFormOpen({
|
|
886
|
+
open: true,
|
|
887
|
+
place: location,
|
|
888
|
+
}), 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
889
|
};
|
|
954
890
|
/**
|
|
955
891
|
*
|