homeflowjs 1.0.102 → 1.0.103
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.
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useState, useRef, useEffect, useCallback,
|
|
3
|
+
useMemo,
|
|
4
|
+
} from 'react';
|
|
5
|
+
import PropTypes from 'prop-types';
|
|
6
|
+
import { useDispatch } from 'react-redux';
|
|
7
|
+
import Autosuggest from 'react-autosuggest';
|
|
8
|
+
import debounce from 'lodash.debounce';
|
|
9
|
+
import { notify } from 'homeflowjs';
|
|
10
|
+
|
|
11
|
+
import { DEBOUNCE_DELAY } from '../../utils';
|
|
12
|
+
import branchSearchUtils from '../../utils/requests-handler';
|
|
13
|
+
import { setSearchedBranchesIDs } from '../../actions/branches.actions';
|
|
14
|
+
import LoadingIcon from '../../shared/loading-icon/loading-icon.component';
|
|
15
|
+
|
|
16
|
+
const { fetchAddressSuggestions, fetchBranchesDataBySearchedAddress, ERROR_DEFAULT_MESSAGE } = branchSearchUtils;
|
|
17
|
+
|
|
18
|
+
const BranchesPolygonSearchInputMultiple = ({
|
|
19
|
+
placeholder,
|
|
20
|
+
inputProps,
|
|
21
|
+
channel,
|
|
22
|
+
isDepartmentSearch,
|
|
23
|
+
hiddenBranchIDs,
|
|
24
|
+
icon: Icon,
|
|
25
|
+
loadingIcon: CustomLoadingIcon,
|
|
26
|
+
handleInputClick,
|
|
27
|
+
handleSuggestionSelected,
|
|
28
|
+
usePolygonSearch,
|
|
29
|
+
}) => {
|
|
30
|
+
const dispatch = useDispatch();
|
|
31
|
+
|
|
32
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
33
|
+
const [searchedAddress, setSearchedAddress] = useState('');
|
|
34
|
+
const [requestIsLoading, setRequestIsLoading] = useState(false);
|
|
35
|
+
const [isFieldDisabled, setIsFieldDisabled] = useState(false);
|
|
36
|
+
const [selectedSuggestion, setSelectedSuggestion] = useState(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
dispatch(setSearchedBranchesIDs(null));
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
const handleSearchedBranchData = (branchesIDs) => {
|
|
43
|
+
if (!branchesIDs) return;
|
|
44
|
+
|
|
45
|
+
const searchedBranchesIDs = branchesIDs.filter(
|
|
46
|
+
(branchData) => !hiddenBranchIDs.includes(branchData.branchID || branchData.id)
|
|
47
|
+
);
|
|
48
|
+
dispatch(setSearchedBranchesIDs(searchedBranchesIDs));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const resetLoadingStatus = () => {
|
|
52
|
+
setRequestIsLoading(false);
|
|
53
|
+
setIsFieldDisabled(false);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const handleError = (err) => {
|
|
57
|
+
let errorData = err;
|
|
58
|
+
if (
|
|
59
|
+
errorData instanceof SyntaxError
|
|
60
|
+
|| errorData instanceof TypeError
|
|
61
|
+
|| errorData instanceof ReferenceError
|
|
62
|
+
) {
|
|
63
|
+
errorData = new Error(ERROR_DEFAULT_MESSAGE);
|
|
64
|
+
}
|
|
65
|
+
notify(errorData.message, 'error')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const polygonRequestPrm = usePolygonSearch ? { usingPolygon: true } : {};
|
|
69
|
+
|
|
70
|
+
const getSearchedBranch = () => {
|
|
71
|
+
if (channel) {
|
|
72
|
+
fetchBranchesDataBySearchedAddress({
|
|
73
|
+
addressID: selectedSuggestion,
|
|
74
|
+
channel,
|
|
75
|
+
isDepartmentSearch,
|
|
76
|
+
...polygonRequestPrm,
|
|
77
|
+
})
|
|
78
|
+
.then(handleSearchedBranchData)
|
|
79
|
+
.catch(handleError)
|
|
80
|
+
.finally(resetLoadingStatus)
|
|
81
|
+
} else {
|
|
82
|
+
Promise.all([
|
|
83
|
+
fetchBranchesDataBySearchedAddress({
|
|
84
|
+
addressID: selectedSuggestion,
|
|
85
|
+
channel: 'sales',
|
|
86
|
+
isDepartmentSearch,
|
|
87
|
+
...polygonRequestPrm,
|
|
88
|
+
}),
|
|
89
|
+
fetchBranchesDataBySearchedAddress({
|
|
90
|
+
addressID: selectedSuggestion,
|
|
91
|
+
channel: 'lettings',
|
|
92
|
+
isDepartmentSearch,
|
|
93
|
+
...polygonRequestPrm,
|
|
94
|
+
})
|
|
95
|
+
])
|
|
96
|
+
.then(([salesBranchIDList, lettingsBranchIDList]) => {
|
|
97
|
+
const mergedList = [...salesBranchIDList, ...lettingsBranchIDList];
|
|
98
|
+
const uniqueBranchesMap = new Map(mergedList.map(branch => [branch.id, branch]));
|
|
99
|
+
const uniqueBranches = Array.from(uniqueBranchesMap.values());
|
|
100
|
+
handleSearchedBranchData(uniqueBranches)
|
|
101
|
+
})
|
|
102
|
+
.catch(handleError)
|
|
103
|
+
.finally(resetLoadingStatus);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (selectedSuggestion && !requestIsLoading) {
|
|
109
|
+
setRequestIsLoading(true);
|
|
110
|
+
setIsFieldDisabled(true);
|
|
111
|
+
|
|
112
|
+
getSearchedBranch();
|
|
113
|
+
}
|
|
114
|
+
}, [channel, selectedSuggestion]);
|
|
115
|
+
|
|
116
|
+
const getSuggestions = useCallback(
|
|
117
|
+
(searchedAddress) => {
|
|
118
|
+
setSelectedSuggestion(null);
|
|
119
|
+
setRequestIsLoading(true);
|
|
120
|
+
fetchAddressSuggestions({ searchedAddress })
|
|
121
|
+
.then((suggestions) => {
|
|
122
|
+
setRequestIsLoading(false);
|
|
123
|
+
setSuggestions(suggestions)
|
|
124
|
+
})
|
|
125
|
+
.catch((err) => notify(err.message, 'error'))
|
|
126
|
+
}, [])
|
|
127
|
+
|
|
128
|
+
const debouncedGetSuggestions = useCallback(debounce(({ value }) => {
|
|
129
|
+
setSearchedAddress(value);
|
|
130
|
+
getSuggestions(value);
|
|
131
|
+
}, DEBOUNCE_DELAY), []);
|
|
132
|
+
|
|
133
|
+
const onSuggestionSelected = useCallback((_, { suggestion }) => {
|
|
134
|
+
if (suggestion.id) {
|
|
135
|
+
if (handleSuggestionSelected) {
|
|
136
|
+
handleSuggestionSelected();
|
|
137
|
+
}
|
|
138
|
+
setSelectedSuggestion(suggestion.id);
|
|
139
|
+
}
|
|
140
|
+
}, [channel]);
|
|
141
|
+
|
|
142
|
+
const onSuggestionsClear = useCallback(() => setSuggestions([]), []);
|
|
143
|
+
|
|
144
|
+
const getSuggestionValue = useCallback((suggestion) => suggestion.address, []);
|
|
145
|
+
|
|
146
|
+
const renderSuggestion = useCallback((suggestion) => (
|
|
147
|
+
<span className="react-autosuggest_span">{suggestion.address}</span>
|
|
148
|
+
), []);
|
|
149
|
+
|
|
150
|
+
const onInputChange = useCallback((_, { newValue }) => {
|
|
151
|
+
if (!newValue) {
|
|
152
|
+
dispatch(setSearchedBranchesIDs(null));
|
|
153
|
+
}
|
|
154
|
+
setSearchedAddress(newValue);
|
|
155
|
+
setSelectedSuggestion(null);
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
158
|
+
const onInputClick = useCallback(() => {
|
|
159
|
+
if (handleInputClick) {
|
|
160
|
+
handleInputClick();
|
|
161
|
+
}
|
|
162
|
+
}, [handleInputClick]);
|
|
163
|
+
|
|
164
|
+
const additionalInputProps = useMemo(() => ({
|
|
165
|
+
placeholder,
|
|
166
|
+
name: 'react-autosuggest__input',
|
|
167
|
+
id: 'react-autosuggest__input',
|
|
168
|
+
value: searchedAddress,
|
|
169
|
+
onClick: onInputClick,
|
|
170
|
+
onChange: onInputChange,
|
|
171
|
+
...((isFieldDisabled && requestIsLoading) ? { disabled: true } : {}),
|
|
172
|
+
}), [searchedAddress, isFieldDisabled, requestIsLoading, onInputClick]);
|
|
173
|
+
|
|
174
|
+
const SearchLoadingIcon = CustomLoadingIcon || LoadingIcon;
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<>
|
|
178
|
+
<input type="hidden" name="location" value={searchedAddress} />
|
|
179
|
+
<Autosuggest
|
|
180
|
+
suggestions={suggestions}
|
|
181
|
+
onSuggestionsClearRequested={onSuggestionsClear}
|
|
182
|
+
onSuggestionsFetchRequested={debouncedGetSuggestions}
|
|
183
|
+
onSuggestionSelected={onSuggestionSelected}
|
|
184
|
+
getSuggestionValue={getSuggestionValue}
|
|
185
|
+
renderSuggestion={renderSuggestion}
|
|
186
|
+
inputProps={{
|
|
187
|
+
...additionalInputProps,
|
|
188
|
+
...inputProps,
|
|
189
|
+
}}
|
|
190
|
+
highlightFirstSuggestion
|
|
191
|
+
/>
|
|
192
|
+
{requestIsLoading ? <SearchLoadingIcon /> : <Icon />}
|
|
193
|
+
</>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
BranchesPolygonSearchInputMultiple.propTypes = {
|
|
198
|
+
placeholder: PropTypes.string,
|
|
199
|
+
inputProps: PropTypes.object,
|
|
200
|
+
channel: PropTypes.string,
|
|
201
|
+
isDepartmentSearch: PropTypes.bool,
|
|
202
|
+
icon: PropTypes.func,
|
|
203
|
+
loadingIcon: PropTypes.func,
|
|
204
|
+
handleInputClick: PropTypes.func,
|
|
205
|
+
handleSuggestionSelected: PropTypes.func,
|
|
206
|
+
usePolygonSearch: PropTypes.bool,
|
|
207
|
+
hiddenBranchIDs: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
BranchesPolygonSearchInputMultiple.defaultProps = {
|
|
211
|
+
placeholder: '',
|
|
212
|
+
inputProps: {},
|
|
213
|
+
channel: null,
|
|
214
|
+
isDepartmentSearch: true,
|
|
215
|
+
icon: () => <div/>,
|
|
216
|
+
loadingIcon: null,
|
|
217
|
+
handleInputClick: null,
|
|
218
|
+
handleSuggestionSelected: null,
|
|
219
|
+
hiddenBranchIDs: [],
|
|
220
|
+
usePolygonSearch: true,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export default BranchesPolygonSearchInputMultiple;
|
|
@@ -103,7 +103,7 @@ const BranchesPolygonSearchInput = ({
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
useEffect(() => {
|
|
106
|
-
if (selectedSuggestion) {
|
|
106
|
+
if (selectedSuggestion && !requestIsLoading) {
|
|
107
107
|
setRequestIsLoading(true);
|
|
108
108
|
setIsFieldDisabled(true);
|
|
109
109
|
|
|
@@ -201,7 +201,8 @@ BranchesPolygonSearchInput.propTypes = {
|
|
|
201
201
|
loadingIcon: PropTypes.func,
|
|
202
202
|
handleInputClick: PropTypes.func,
|
|
203
203
|
handleSuggestionSelected: PropTypes.func,
|
|
204
|
-
|
|
204
|
+
usePolygonSearch: PropTypes.bool,
|
|
205
|
+
hiddenBranchIDs: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
|
205
206
|
};
|
|
206
207
|
|
|
207
208
|
BranchesPolygonSearchInput.defaultProps = {
|
|
@@ -213,7 +214,7 @@ BranchesPolygonSearchInput.defaultProps = {
|
|
|
213
214
|
loadingIcon: null,
|
|
214
215
|
handleInputClick: null,
|
|
215
216
|
handleSuggestionSelected: null,
|
|
216
|
-
|
|
217
|
+
hiddenBranchIDs: [],
|
|
217
218
|
usePolygonSearch: true,
|
|
218
219
|
};
|
|
219
220
|
|
package/branches/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import BranchStreetview from './branch-streetview/branch-streetview.component';
|
|
|
4
4
|
import BranchesSearchForm from './branches-search-form/branches-search-form.component';
|
|
5
5
|
import BranchesSearchInput from './branches-search-form/branches-search-input.component';
|
|
6
6
|
import BranchesPolygonSearchInput from './branches-search-form/branches-polygon-search-input.component';
|
|
7
|
+
import BranchesPolygonSearchInputMultiple from './branches-search-form/branches-polygon-search-input-multiple.component';
|
|
7
8
|
|
|
8
9
|
export {
|
|
9
10
|
BranchesMap,
|
|
@@ -11,5 +12,6 @@ export {
|
|
|
11
12
|
BranchStreetview,
|
|
12
13
|
BranchesSearchForm,
|
|
13
14
|
BranchesSearchInput,
|
|
14
|
-
BranchesPolygonSearchInput
|
|
15
|
+
BranchesPolygonSearchInput,
|
|
16
|
+
BranchesPolygonSearchInputMultiple
|
|
15
17
|
};
|
package/package.json
CHANGED
|
@@ -47,6 +47,25 @@ const fetchDetailedAddressData = ({ addressID }) => fetch(`/address_lookup?id=${
|
|
|
47
47
|
return err;
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
const fetchBranchesDataByPostcode = ({
|
|
51
|
+
postcode,
|
|
52
|
+
usingPolygon = false,
|
|
53
|
+
channel,
|
|
54
|
+
isDepartmentSearch
|
|
55
|
+
}) => {
|
|
56
|
+
const polygonPrm = (usingPolygon && channel) ? '&using_polygon=true' : '';
|
|
57
|
+
const channelPrm = channel ? `&channel=${channel}` : '';
|
|
58
|
+
const departmentPrm = (isDepartmentSearch && channel) ? `&department=true` : '';
|
|
59
|
+
const crossAgencyPrm = `&check_for_cross_agency_branches=true`
|
|
60
|
+
|
|
61
|
+
return fetch(`/postcode_branches_lookup?postcode=${postcode}${departmentPrm}${channelPrm}${polygonPrm}${crossAgencyPrm}`)
|
|
62
|
+
.then(handleRequest)
|
|
63
|
+
.catch((err) => {
|
|
64
|
+
console.log(err);
|
|
65
|
+
return err;
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
const fetchBranchDataBySearchedAddress = ({
|
|
51
70
|
addressID,
|
|
52
71
|
usingPolygon,
|
|
@@ -71,8 +90,56 @@ const fetchBranchDataBySearchedAddress = ({
|
|
|
71
90
|
return err;
|
|
72
91
|
});
|
|
73
92
|
|
|
93
|
+
const fetchBranchesDataBySearchedAddress = ({
|
|
94
|
+
addressID,
|
|
95
|
+
usingPolygon,
|
|
96
|
+
channel,
|
|
97
|
+
isDepartmentSearch,
|
|
98
|
+
}) => fetchDetailedAddressData({ addressID })
|
|
99
|
+
.then((data) => {
|
|
100
|
+
if (data?.postcode) {
|
|
101
|
+
return fetchBranchesDataByPostcode({
|
|
102
|
+
postcode: data.postcode,
|
|
103
|
+
usingPolygon,
|
|
104
|
+
channel,
|
|
105
|
+
isDepartmentSearch
|
|
106
|
+
})
|
|
107
|
+
} else {
|
|
108
|
+
throw new Error(ERROR_MESSAGE_NOT_FOUND_POSTCODE);
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.then((data) => {
|
|
112
|
+
const branchesArray = Array.isArray(data) ? data : (data?.branches || []);
|
|
113
|
+
return branchesArray.map(branch => ({
|
|
114
|
+
id: branch?.id?.toString(),
|
|
115
|
+
distance: branch?.distance,
|
|
116
|
+
branchID: branch?.id?.toString(),
|
|
117
|
+
name: branch?.name,
|
|
118
|
+
address: [branch?.street_address, branch?.town, branch?.county, branch?.postcode].filter(Boolean).join(', '),
|
|
119
|
+
postcode: branch?.postcode,
|
|
120
|
+
telephone: branch?.telephone,
|
|
121
|
+
email: branch?.email,
|
|
122
|
+
agencyId: branch?.agency_id,
|
|
123
|
+
agencyName: branch?.agency_name,
|
|
124
|
+
salesEnabled: branch?.sales_enabled,
|
|
125
|
+
lettingsEnabled: branch?.lettings_enabled,
|
|
126
|
+
salesContactNumber: branch?.contactable_sales_telephone,
|
|
127
|
+
lettingsContactNumber: branch?.contactable_lettings_telephone,
|
|
128
|
+
heroImageURL: branch?.hero_asset?.mr_url || null,
|
|
129
|
+
departments: branch?.departments,
|
|
130
|
+
branchURL: branch?.url_label,
|
|
131
|
+
externalBranchURL: branch?.external_branch_url,
|
|
132
|
+
locationMethod: branch?.location_method,
|
|
133
|
+
})).filter(branch => branch.id);
|
|
134
|
+
})
|
|
135
|
+
.catch((err) => {
|
|
136
|
+
console.log(err);
|
|
137
|
+
return err;
|
|
138
|
+
});
|
|
139
|
+
|
|
74
140
|
export default {
|
|
75
141
|
fetchAddressSuggestions,
|
|
76
142
|
fetchBranchDataBySearchedAddress,
|
|
143
|
+
fetchBranchesDataBySearchedAddress,
|
|
77
144
|
ERROR_DEFAULT_MESSAGE,
|
|
78
145
|
};
|