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
- hiddenBranchIds: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
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
- hiddenBranchIds: [],
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "1.0.102",
3
+ "version": "1.0.103",
4
4
  "sideEffects": [
5
5
  "modal/**/*",
6
6
  "user/default-profile/**/*",
@@ -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
  };