homeflowjs 1.0.77 → 1.0.78

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.
@@ -14,3 +14,13 @@ export const setBranchesPagination = (payload) => ({
14
14
  type: BranchesActionTypes.SET_BRANCHES_PAGINATION,
15
15
  payload,
16
16
  });
17
+
18
+ export const setSearchedPolygonBranchesIDs = (payload) => ({
19
+ type: BranchesActionTypes.SET_SEARCHED_POLYGON_BRANCHES_IDS,
20
+ payload,
21
+ });
22
+
23
+ export const setBranchDepartmentFilter = (payload) => ({
24
+ type: BranchesActionTypes.SET_BRANCH_DEPARTMENT_FILTER,
25
+ payload,
26
+ });
@@ -2,6 +2,8 @@ const BranchesActionTypes = {
2
2
  SET_BRANCHES: 'SET_BRANCHES',
3
3
  SET_BRANCHES_SEARCH: 'SET_BRANCHES_SEARCH',
4
4
  SET_BRANCHES_PAGINATION: 'SET_BRANCHES_PAGINATION',
5
+ SET_SEARCHED_POLYGON_BRANCHES_IDS: 'SET_SEARCHED_POLYGON_BRANCHES_IDS',
6
+ SET_BRANCH_DEPARTMENT_FILTER: 'SET_BRANCH_DEPARTMENT_FILTER',
5
7
  };
6
8
 
7
9
  export default BranchesActionTypes;
@@ -0,0 +1,216 @@
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 { setSearchedPolygonBranchesIDs } from '../../actions/branches.actions';
14
+ import LoadingIcon from '../../shared/loading-icon/loading-icon.component';
15
+
16
+ const { fetchAddressSuggestions, fetchBranchDataBySearchedAddress, ERROR_DEFAULT_MESSAGE } = branchSearchUtils;
17
+
18
+ const BranchesPolygonSearchInput = ({
19
+ placeholder,
20
+ inputProps,
21
+ channel,
22
+ isDepartmentSearch,
23
+ hiddenBranchIDs,
24
+ icon: Icon,
25
+ loadingIcon: CustomLoadingIcon,
26
+ handleInputClick,
27
+ handleSuggestionSelected,
28
+ }) => {
29
+ const dispatch = useDispatch();
30
+
31
+ const [suggestions, setSuggestions] = useState([]);
32
+ const [searchedAddress, setSearchedAddress] = useState('');
33
+ const [requestIsLoading, setRequestIsLoading] = useState(false);
34
+ const [isFieldDisabled, setIsFieldDisabled] = useState(false);
35
+ const [selectedSuggestion, setSelectedSuggestion] = useState(null);
36
+
37
+ useEffect(() => {
38
+ dispatch(setSearchedPolygonBranchesIDs(null));
39
+ }, []);
40
+
41
+ const handleSearchedBranchData = (branchesIDs) => {
42
+ const searchedBranchesIDs = branchesIDs.filter(
43
+ (branchID) => !hiddenBranchIDs.includes(branchID.toString())
44
+ );
45
+ dispatch(setSearchedPolygonBranchesIDs(searchedBranchesIDs));
46
+ };
47
+
48
+ const resetLoadingStatus = () => {
49
+ setRequestIsLoading(false);
50
+ setIsFieldDisabled(false);
51
+ }
52
+
53
+ const handleError = (err) => {
54
+ let errorData = err;
55
+ if (
56
+ errorData instanceof SyntaxError
57
+ || errorData instanceof TypeError
58
+ || errorData instanceof ReferenceError
59
+ ) {
60
+ errorData = new Error(ERROR_DEFAULT_MESSAGE);
61
+ }
62
+ notify(errorData.message, 'error')
63
+ }
64
+
65
+ const getSearchedBranch = () => {
66
+ if (channel) {
67
+ fetchBranchDataBySearchedAddress({
68
+ addressID: selectedSuggestion,
69
+ usingPolygon: true,
70
+ channel,
71
+ isDepartmentSearch,
72
+ })
73
+ .then(handleSearchedBranchData)
74
+ .catch(handleError)
75
+ .finally(resetLoadingStatus)
76
+ } else {
77
+ Promise.all([
78
+ fetchBranchDataBySearchedAddress({
79
+ addressID: selectedSuggestion,
80
+ usingPolygon: true,
81
+ channel: 'sales',
82
+ isDepartmentSearch,
83
+ }),
84
+ fetchBranchDataBySearchedAddress({
85
+ addressID: selectedSuggestion,
86
+ usingPolygon: true,
87
+ channel: 'lettings',
88
+ isDepartmentSearch,
89
+ })
90
+ ])
91
+ .then(([salesBranchIDList, lettingsBranchIDList]) => {
92
+ handleSearchedBranchData([
93
+ ...salesBranchIDList,
94
+ ...lettingsBranchIDList,
95
+ ])
96
+ })
97
+ .catch(handleError)
98
+ .finally(resetLoadingStatus);
99
+ }
100
+ }
101
+
102
+ useEffect(() => {
103
+ if (selectedSuggestion) {
104
+ setRequestIsLoading(true);
105
+ setIsFieldDisabled(true);
106
+
107
+ getSearchedBranch();
108
+ }
109
+ }, [channel, selectedSuggestion]);
110
+
111
+ const getSuggestions = useCallback(
112
+ (searchedAddress) => {
113
+ setSelectedSuggestion(null);
114
+ setRequestIsLoading(true);
115
+ fetchAddressSuggestions({ searchedAddress })
116
+ .then((suggestions) => {
117
+ setRequestIsLoading(false);
118
+ setSuggestions(suggestions)
119
+ })
120
+ .catch((err) => notify(err.message, 'error'))
121
+ }, [])
122
+
123
+ const debouncedGetSuggestions = useCallback(debounce(({ value }) => {
124
+ setSearchedAddress(value);
125
+ getSuggestions(value);
126
+ }, DEBOUNCE_DELAY), []);
127
+
128
+ const onSuggestionSelected = useCallback((_, { suggestion }) => {
129
+ if (suggestion.id) {
130
+ if (handleSuggestionSelected) {
131
+ handleSuggestionSelected();
132
+ }
133
+ setSelectedSuggestion(suggestion.id);
134
+ }
135
+ }, [channel]);
136
+
137
+ const onSuggestionsClear = useCallback(() => setSuggestions([]), []);
138
+
139
+ const getSuggestionValue = useCallback((suggestion) => suggestion.address, []);
140
+
141
+ const renderSuggestion = useCallback((suggestion) => (
142
+ <span className="react-autosuggest_span">{suggestion.address}</span>
143
+ ), []);
144
+
145
+ const onInputChange = useCallback((_, { newValue }) => {
146
+ if (!newValue) {
147
+ dispatch(setSearchedPolygonBranchesIDs(null));
148
+ }
149
+ setSearchedAddress(newValue);
150
+ setSelectedSuggestion(null);
151
+ }, []);
152
+
153
+ const onInputClick = useCallback(() => {
154
+ if (handleInputClick) {
155
+ handleInputClick();
156
+ }
157
+ }, [handleInputClick]);
158
+
159
+ const additionalInputProps = useMemo(() => ({
160
+ placeholder,
161
+ name: 'react-autosuggest__input',
162
+ id: 'react-autosuggest__input',
163
+ value: searchedAddress,
164
+ onClick: onInputClick,
165
+ onChange: onInputChange,
166
+ ...((isFieldDisabled && requestIsLoading) ? { disabled: true } : {}),
167
+ }), [searchedAddress, isFieldDisabled, requestIsLoading, onInputClick]);
168
+
169
+ const SearchLoadingIcon = CustomLoadingIcon || LoadingIcon;
170
+
171
+ return (
172
+ <>
173
+ <input type="hidden" name="location" value={searchedAddress} />
174
+ <Autosuggest
175
+ suggestions={suggestions}
176
+ onSuggestionsClearRequested={onSuggestionsClear}
177
+ onSuggestionsFetchRequested={debouncedGetSuggestions}
178
+ onSuggestionSelected={onSuggestionSelected}
179
+ getSuggestionValue={getSuggestionValue}
180
+ renderSuggestion={renderSuggestion}
181
+ inputProps={{
182
+ ...additionalInputProps,
183
+ ...inputProps,
184
+ }}
185
+ highlightFirstSuggestion
186
+ />
187
+ {requestIsLoading ? <SearchLoadingIcon /> : <Icon />}
188
+ </>
189
+ );
190
+ };
191
+
192
+ BranchesPolygonSearchInput.propTypes = {
193
+ placeholder: PropTypes.string,
194
+ inputProps: PropTypes.object,
195
+ channel: PropTypes.string,
196
+ isDepartmentSearch: PropTypes.bool,
197
+ icon: PropTypes.func,
198
+ loadingIcon: PropTypes.func,
199
+ handleInputClick: PropTypes.func,
200
+ handleSuggestionSelected: PropTypes.func,
201
+ hiddenBranchIds: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
202
+ };
203
+
204
+ BranchesPolygonSearchInput.defaultProps = {
205
+ placeholder: '',
206
+ inputProps: {},
207
+ channel: null,
208
+ isDepartmentSearch: true,
209
+ icon: () => <div/>,
210
+ loadingIcon: null,
211
+ handleInputClick: null,
212
+ handleSuggestionSelected: null,
213
+ hiddenBranchIds: [],
214
+ };
215
+
216
+ export default BranchesPolygonSearchInput;
package/branches/index.js CHANGED
@@ -3,6 +3,7 @@ import BranchMap from './branch-map/branch-map.component';
3
3
  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
+ import BranchesPolygonSearchInput from './branches-search-form/branches-polygon-search-input.component';
6
7
 
7
8
  export {
8
9
  BranchesMap,
@@ -10,4 +11,5 @@ export {
10
11
  BranchStreetview,
11
12
  BranchesSearchForm,
12
13
  BranchesSearchInput,
14
+ BranchesPolygonSearchInput
13
15
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "1.0.77",
3
+ "version": "1.0.78",
4
4
  "sideEffects": [
5
5
  "modal/**/*",
6
6
  "user/default-profile/**/*",
@@ -4,6 +4,8 @@ const INITIAL_STATE = {
4
4
  branches: [],
5
5
  branchesSearch: '',
6
6
  pagination: {},
7
+ searchedBranchesIDs: null,
8
+ branchDepartmentFilter: 'all',
7
9
  };
8
10
 
9
11
  const branchesReducer = (state = INITIAL_STATE, action) => {
@@ -23,6 +25,16 @@ const branchesReducer = (state = INITIAL_STATE, action) => {
23
25
  ...state,
24
26
  pagination: action.payload,
25
27
  };
28
+ case BranchesActionTypes.SET_SEARCHED_POLYGON_BRANCHES_IDS:
29
+ return {
30
+ ...state,
31
+ searchedBranchesIDs: action.payload,
32
+ };
33
+ case BranchesActionTypes.SET_BRANCH_DEPARTMENT_FILTER:
34
+ return {
35
+ ...state,
36
+ branchDepartmentFilter: action.payload,
37
+ };
26
38
  default: return state;
27
39
  }
28
40
  };
@@ -0,0 +1,78 @@
1
+ const ERROR_MESSAGE_SERVICE_NOT_AVAILABLE = 'Sorry, the service is temporarily unavailable. Please try again later.';
2
+ const ERROR_MESSAGE_NOT_FOUND_POSTCODE = 'Sorry, no postcode was found for the searched address.';
3
+ const ERROR_DEFAULT_MESSAGE = 'Sorry, something went wrong. Please try again later.';
4
+
5
+ const handleRequest = async(response) => {
6
+ if (response.ok) {
7
+ const responseData = await response.json();
8
+ return responseData;
9
+ } else {
10
+ throw new Error(ERROR_MESSAGE_SERVICE_NOT_AVAILABLE);
11
+ }
12
+ }
13
+
14
+ const fetchAddressSuggestions = ({ searchedAddress }) => fetch(`/address_lookup?autocomplete=${searchedAddress}`)
15
+ .then(handleRequest)
16
+ .then((data) => data?.suggestions
17
+ ? data?.suggestions?.filter((_item, index) => index < 15)
18
+ : []
19
+ )
20
+ .catch((err) => {
21
+ console.log(err);
22
+ return err;
23
+ })
24
+
25
+ const fetchBranchDataByPostcode = ({
26
+ postcode,
27
+ usingPolygon = false,
28
+ channel,
29
+ isDepartmentSearch
30
+ }) => {
31
+ const polygonPrm = (usingPolygon && channel) ? '&using_polygon=true' : '';
32
+ const channelPrm = channel ? `&channel=${channel}` : '';
33
+ const departmentPrm = (isDepartmentSearch && channel) ? `&department=true` : '';
34
+
35
+ return fetch(`/postcode_branch_lookup?postcode=${postcode}${departmentPrm}${channelPrm}${polygonPrm}`)
36
+ .then(handleRequest)
37
+ .catch((err) => {
38
+ console.log(err);
39
+ return err;
40
+ })
41
+ }
42
+
43
+ const fetchDetailedAddressData = ({ addressID }) => fetch(`/address_lookup?id=${addressID}`)
44
+ .then(handleRequest)
45
+ .catch((err) => {
46
+ console.log(err);
47
+ return err;
48
+ });
49
+
50
+ const fetchBranchDataBySearchedAddress = ({
51
+ addressID,
52
+ usingPolygon,
53
+ channel,
54
+ isDepartmentSearch,
55
+ }) => fetchDetailedAddressData({ addressID })
56
+ .then((data) => {
57
+ if (data?.postcode) {
58
+ return fetchBranchDataByPostcode({
59
+ postcode: data.postcode,
60
+ usingPolygon,
61
+ channel,
62
+ isDepartmentSearch
63
+ })
64
+ } else {
65
+ throw new Error(ERROR_MESSAGE_NOT_FOUND_POSTCODE);
66
+ }
67
+ })
68
+ .then((data) => data?.branch && data?.branch?.id ? [data?.branch?.id.toString()] : [])
69
+ .catch((err) => {
70
+ console.log(err);
71
+ return err;
72
+ });
73
+
74
+ export default {
75
+ fetchAddressSuggestions,
76
+ fetchBranchDataBySearchedAddress,
77
+ ERROR_DEFAULT_MESSAGE,
78
+ };