homeflowjs 0.9.16 → 0.9.17
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/package.json +1 -1
- package/reducers/user.reducer.js +49 -4
- package/search/price-select/price-select.component.jsx +2 -2
- package/search/price-select/price-select.test.js +2 -2
- package/user/marketing-preferences-form/marketing-preferences-form.component.jsx +32 -14
- package/user/reset-password-form/reset-password-form.component.jsx +11 -1
- package/user/sign-out-button/sign-out-button.component.jsx +5 -2
- package/user/user-input/user-input.component.jsx +28 -8
- package/user/user-register-form/user-register-form.component.jsx +7 -1
package/package.json
CHANGED
package/reducers/user.reducer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable camelcase */
|
|
1
2
|
import UserActionTypes from '../actions/user.types';
|
|
2
3
|
|
|
3
4
|
// currentUser is user loaded from server
|
|
@@ -5,7 +6,7 @@ import UserActionTypes from '../actions/user.types';
|
|
|
5
6
|
|
|
6
7
|
export const INITIAL_USER_DATA = {
|
|
7
8
|
address_2: null,
|
|
8
|
-
address_html:
|
|
9
|
+
address_html: '',
|
|
9
10
|
agency: { hfpe_enabled: null, name: null, email: null },
|
|
10
11
|
county: null,
|
|
11
12
|
custom_marketing_preferences: null,
|
|
@@ -29,6 +30,7 @@ export const INITIAL_USER_DATA = {
|
|
|
29
30
|
user_id: null,
|
|
30
31
|
password: '',
|
|
31
32
|
password_confirmation: '',
|
|
33
|
+
marketing_preferences: {},
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
// set both currentUser and localUser to same initial data
|
|
@@ -43,7 +45,7 @@ const INITIAL_STATE = {
|
|
|
43
45
|
userCredentials: {
|
|
44
46
|
email: '',
|
|
45
47
|
password: '',
|
|
46
|
-
}
|
|
48
|
+
},
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
// TODO: load user from localStorage if not logged in
|
|
@@ -56,12 +58,55 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|
|
56
58
|
currentUser: action.payload,
|
|
57
59
|
};
|
|
58
60
|
case UserActionTypes.SET_LOCAL_USER:
|
|
61
|
+
if (Object.prototype.hasOwnProperty.call(action.payload, 'opt_in_terms')) {
|
|
62
|
+
return {
|
|
63
|
+
...state,
|
|
64
|
+
localUser: {
|
|
65
|
+
...state.localUser,
|
|
66
|
+
marketing_preferences: {
|
|
67
|
+
...state.localUser.marketing_preferences,
|
|
68
|
+
...action.payload,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(action.payload, 'opt_in_marketing_url')) {
|
|
74
|
+
/**
|
|
75
|
+
* opt_in_marketing_statement opt_in_marketing and opt_in_marketing_url belong all to
|
|
76
|
+
* the same object, this will update the url and value of marketing_preferences if
|
|
77
|
+
* opted in only, the statement will get added by the UserRegisterForm component
|
|
78
|
+
* since it has to be registered regardless the choosen option.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
const { opt_in_marketing_url } = action.payload;
|
|
82
|
+
const marketingPreferencesObj = {
|
|
83
|
+
opt_in_marketing: '',
|
|
84
|
+
opt_in_marketing_url: '',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (opt_in_marketing_url) {
|
|
88
|
+
marketingPreferencesObj.opt_in_marketing = 'on';
|
|
89
|
+
|
|
90
|
+
marketingPreferencesObj.opt_in_marketing_url = opt_in_marketing_url;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...state,
|
|
95
|
+
localUser: {
|
|
96
|
+
...state.localUser,
|
|
97
|
+
marketing_preferences: {
|
|
98
|
+
...state.localUser.marketing_preferences,
|
|
99
|
+
...marketingPreferencesObj,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
59
104
|
return {
|
|
60
105
|
...state,
|
|
61
106
|
localUser: {
|
|
62
107
|
...state.localUser,
|
|
63
108
|
...action.payload,
|
|
64
|
-
|
|
109
|
+
},
|
|
65
110
|
};
|
|
66
111
|
case UserActionTypes.SET_USER_CREDENTIALS:
|
|
67
112
|
return {
|
|
@@ -74,6 +119,6 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|
|
74
119
|
default:
|
|
75
120
|
return state;
|
|
76
121
|
}
|
|
77
|
-
}
|
|
122
|
+
};
|
|
78
123
|
|
|
79
124
|
export default userReducer;
|
|
@@ -108,11 +108,11 @@ const PriceSelect = (props) => {
|
|
|
108
108
|
const selectedPrice = type === 'min' ? minPrice : maxPrice;
|
|
109
109
|
|
|
110
110
|
if (type === 'max' && minPrice) {
|
|
111
|
-
prices = prices.filter(price => price
|
|
111
|
+
prices = prices.filter(price => price >= minPrice);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
if (type === 'min' && maxPrice) {
|
|
115
|
-
prices = prices.filter(price => price
|
|
115
|
+
prices = prices.filter(price => price <= maxPrice);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
if (reactSelect) {
|
|
@@ -44,7 +44,7 @@ describe('PriceSelect', () => {
|
|
|
44
44
|
const wrapper = mount(<PriceSelect {...testProps} />);
|
|
45
45
|
|
|
46
46
|
// includes the default unselected option
|
|
47
|
-
expect(wrapper.find('option').length).toEqual(
|
|
47
|
+
expect(wrapper.find('option').length).toEqual(3);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('removes higher prices for min when selected for max', () => {
|
|
@@ -60,7 +60,7 @@ describe('PriceSelect', () => {
|
|
|
60
60
|
const wrapper = mount(<PriceSelect {...testProps} />);
|
|
61
61
|
|
|
62
62
|
// includes the default unselected option
|
|
63
|
-
expect(wrapper.find('option').length).toEqual(
|
|
63
|
+
expect(wrapper.find('option').length).toEqual(4);
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('calls the setSearchField prop on change', () => {
|
|
@@ -1,24 +1,49 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
|
+
import notify from '../../app/notify';
|
|
5
|
+
import { setCurrentUser } from '../../actions/user.actions';
|
|
4
6
|
|
|
5
|
-
const MarketingPreferencesForm = ({
|
|
7
|
+
const MarketingPreferencesForm = ({ buttonClass, buttonSpanClass }) => {
|
|
8
|
+
const user = useSelector((state) => state.user.currentUser);
|
|
6
9
|
const [optInMarketing, setOptInMarketing] = useState(user.is_opted_in_to_marketing);
|
|
7
10
|
const contactOption = user.is_opted_in_to_marketing ? 'OPTED IN to' : 'OPTED OUT of';
|
|
11
|
+
const dispatch = useDispatch();
|
|
8
12
|
|
|
9
13
|
const handleChange = (e) => {
|
|
10
14
|
setOptInMarketing(e.target.value === 'optin');
|
|
11
15
|
};
|
|
12
16
|
|
|
13
17
|
const handleSubmit = (e) => {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
|
|
20
|
+
if (!user.marketing_preference_id) {
|
|
21
|
+
notify('Please sign in to update your preferences', 'error');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
fetch(`/marketing_preferences/${user.marketing_preference_id}`, {
|
|
15
26
|
method: 'PUT',
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
opt_in_marketing_at: optInMarketing ? 'optin' : 'optout',
|
|
18
30
|
opt_in_marketing_url: document.URL,
|
|
19
31
|
user_id: user.user_id,
|
|
20
|
-
},
|
|
21
|
-
}).then((res) =>
|
|
32
|
+
}),
|
|
33
|
+
}).then((res) => {
|
|
34
|
+
if (res.ok && (res.status === 200 || res.status === 204)) {
|
|
35
|
+
dispatch(setCurrentUser({
|
|
36
|
+
...user,
|
|
37
|
+
is_opted_in_to_marketing: optInMarketing,
|
|
38
|
+
}));
|
|
39
|
+
notify('Your preferences have been successfully updated', 'success');
|
|
40
|
+
} else {
|
|
41
|
+
throw res;
|
|
42
|
+
}
|
|
43
|
+
}).catch((error) => {
|
|
44
|
+
notify('There was an error updating your preferences', 'error');
|
|
45
|
+
console.error(error);
|
|
46
|
+
});
|
|
22
47
|
};
|
|
23
48
|
|
|
24
49
|
return (
|
|
@@ -66,7 +91,6 @@ const MarketingPreferencesForm = ({ user, buttonClass, buttonSpanClass }) => {
|
|
|
66
91
|
};
|
|
67
92
|
|
|
68
93
|
MarketingPreferencesForm.propTypes = {
|
|
69
|
-
user: PropTypes.object.isRequired,
|
|
70
94
|
buttonClass: PropTypes.string,
|
|
71
95
|
buttonSpanClass: PropTypes.string,
|
|
72
96
|
};
|
|
@@ -76,13 +100,7 @@ MarketingPreferencesForm.defaultProps = {
|
|
|
76
100
|
buttonSpanClass: '',
|
|
77
101
|
};
|
|
78
102
|
|
|
79
|
-
|
|
80
|
-
user: state.user.currentUser,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export default connect(
|
|
84
|
-
mapStateToProps,
|
|
85
|
-
)(MarketingPreferencesForm);
|
|
103
|
+
export default MarketingPreferencesForm;
|
|
86
104
|
|
|
87
105
|
// From C2
|
|
88
106
|
// $.ajax({
|
|
@@ -6,7 +6,9 @@ import PropTypes from 'prop-types';
|
|
|
6
6
|
import { setCurrentUser } from '../../actions/user.actions';
|
|
7
7
|
import notify from '../../app/notify';
|
|
8
8
|
|
|
9
|
-
const ResetPasswordForm = ({
|
|
9
|
+
const ResetPasswordForm = ({
|
|
10
|
+
user, setCurrentUser, inputClass, buttonClass, buttonSpanClass, pattern, patternTitle,
|
|
11
|
+
}) => {
|
|
10
12
|
const [password, setPassword] = useState('');
|
|
11
13
|
const [passwordConfirmation, setPasswordConfirmation] = useState('');
|
|
12
14
|
const history = useHistory();
|
|
@@ -39,6 +41,8 @@ const ResetPasswordForm = ({ user, setCurrentUser, inputClass, buttonClass, butt
|
|
|
39
41
|
value={password}
|
|
40
42
|
onChange={(e) => setPassword(e.target.value)}
|
|
41
43
|
className={inputClass}
|
|
44
|
+
pattern={pattern}
|
|
45
|
+
title={patternTitle}
|
|
42
46
|
/>
|
|
43
47
|
<input
|
|
44
48
|
name="password_confirmation"
|
|
@@ -47,6 +51,8 @@ const ResetPasswordForm = ({ user, setCurrentUser, inputClass, buttonClass, butt
|
|
|
47
51
|
value={passwordConfirmation}
|
|
48
52
|
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
|
49
53
|
className={inputClass}
|
|
54
|
+
pattern={pattern}
|
|
55
|
+
title={patternTitle}
|
|
50
56
|
/>
|
|
51
57
|
|
|
52
58
|
<button
|
|
@@ -65,12 +71,16 @@ ResetPasswordForm.propTypes = {
|
|
|
65
71
|
inputClass: PropTypes.string,
|
|
66
72
|
buttonClass: PropTypes.string,
|
|
67
73
|
buttonSpanClass: PropTypes.string,
|
|
74
|
+
pattern: PropTypes.string,
|
|
75
|
+
patternTitle: PropTypes.string,
|
|
68
76
|
};
|
|
69
77
|
|
|
70
78
|
ResetPasswordForm.defaultProps = {
|
|
71
79
|
inputClass: '',
|
|
72
80
|
buttonClass: '',
|
|
73
81
|
buttonSpanClass: '',
|
|
82
|
+
pattern: null,
|
|
83
|
+
patternTitle: null,
|
|
74
84
|
};
|
|
75
85
|
|
|
76
86
|
const mapStateToProps = (state) => ({
|
|
@@ -21,7 +21,10 @@ const SignOutButton = ({ signOutUser, children, ...otherProps }) => (
|
|
|
21
21
|
|
|
22
22
|
SignOutButton.propTypes = {
|
|
23
23
|
signOutUser: PropTypes.func.isRequired,
|
|
24
|
-
children: PropTypes.
|
|
24
|
+
children: PropTypes.oneOfType([
|
|
25
|
+
PropTypes.element,
|
|
26
|
+
PropTypes.string,
|
|
27
|
+
]).isRequired,
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
const mapDispatchToProps = {
|
|
@@ -31,4 +34,4 @@ const mapDispatchToProps = {
|
|
|
31
34
|
export default connect(
|
|
32
35
|
null,
|
|
33
36
|
mapDispatchToProps,
|
|
34
|
-
)(SignOutButton)
|
|
37
|
+
)(SignOutButton);
|
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { connect } from 'react-redux';
|
|
3
3
|
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
4
6
|
import { setLocalUser } from '../../actions/user.actions';
|
|
5
7
|
|
|
6
|
-
const UserInput = ({
|
|
8
|
+
const UserInput = ({
|
|
9
|
+
name, value, unmappedValue, setLocalUser, type, ...otherProps
|
|
10
|
+
}) => {
|
|
7
11
|
const handleChange = (e) => {
|
|
8
|
-
|
|
12
|
+
let { value } = e.target;
|
|
13
|
+
if (type === 'checkbox') value = e.target.checked ? value : '';
|
|
14
|
+
setLocalUser({ [name]: value });
|
|
9
15
|
};
|
|
10
16
|
|
|
11
|
-
let
|
|
12
|
-
if (name === 'password' || name === 'password_confirmation')
|
|
13
|
-
if (name === 'email')
|
|
17
|
+
let inputType = type;
|
|
18
|
+
if (name === 'password' || name === 'password_confirmation') inputType = 'password';
|
|
19
|
+
if (name === 'email') inputType = 'email';
|
|
14
20
|
|
|
15
21
|
return (
|
|
16
22
|
<input
|
|
17
23
|
id={`user-input-${name}`}
|
|
18
|
-
type={
|
|
24
|
+
type={inputType}
|
|
19
25
|
name={name}
|
|
20
|
-
value={value}
|
|
26
|
+
value={unmappedValue || value}
|
|
21
27
|
onChange={handleChange}
|
|
22
28
|
{...otherProps}
|
|
23
29
|
/>
|
|
24
30
|
);
|
|
25
31
|
};
|
|
26
32
|
|
|
33
|
+
UserInput.propTypes = {
|
|
34
|
+
name: PropTypes.string.isRequired,
|
|
35
|
+
setLocalUser: PropTypes.func.isRequired,
|
|
36
|
+
type: PropTypes.string,
|
|
37
|
+
unmappedValue: PropTypes.string,
|
|
38
|
+
// eslint-disable-next-line react/require-default-props
|
|
39
|
+
value: PropTypes.any,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
UserInput.defaultProps = {
|
|
43
|
+
type: 'text',
|
|
44
|
+
unmappedValue: null,
|
|
45
|
+
};
|
|
46
|
+
|
|
27
47
|
const mapStateToProps = (state, ownProps) => ({
|
|
28
48
|
value: state.user.localUser[ownProps.name],
|
|
29
49
|
});
|
|
@@ -35,4 +55,4 @@ const mapDispatchToProps = {
|
|
|
35
55
|
export default connect(
|
|
36
56
|
mapStateToProps,
|
|
37
57
|
mapDispatchToProps,
|
|
38
|
-
)(UserInput)
|
|
58
|
+
)(UserInput);
|
|
@@ -14,6 +14,7 @@ const UserRegisterForm = (props) => {
|
|
|
14
14
|
createUser,
|
|
15
15
|
setLoading,
|
|
16
16
|
doNotContact,
|
|
17
|
+
marketingStatement,
|
|
17
18
|
...otherProps
|
|
18
19
|
} = props;
|
|
19
20
|
|
|
@@ -28,6 +29,9 @@ const UserRegisterForm = (props) => {
|
|
|
28
29
|
e.preventDefault();
|
|
29
30
|
|
|
30
31
|
const userParams = doNotContact ? { ...localUser, do_not_contact: 1 } : localUser;
|
|
32
|
+
userParams.marketing_preferences = {
|
|
33
|
+
...localUser.marketing_preferences, opt_in_marketing_statement: marketingStatement,
|
|
34
|
+
};
|
|
31
35
|
|
|
32
36
|
if (validateForm()) {
|
|
33
37
|
setLoading({ userRegister: true });
|
|
@@ -64,13 +68,15 @@ UserRegisterForm.propTypes = {
|
|
|
64
68
|
createUser: PropTypes.func.isRequired,
|
|
65
69
|
setLoading: PropTypes.func.isRequired,
|
|
66
70
|
doNotContact: PropTypes.bool,
|
|
71
|
+
marketingStatement: PropTypes.string,
|
|
67
72
|
};
|
|
68
73
|
|
|
69
74
|
UserRegisterForm.defaultProps = {
|
|
70
75
|
doNotContact: false,
|
|
76
|
+
marketingStatement: 'Marketing preferences communications', // TODO: get generic message and all themes to supply statement.
|
|
71
77
|
};
|
|
72
78
|
|
|
73
|
-
const mapStateToProps = state => ({
|
|
79
|
+
const mapStateToProps = (state) => ({
|
|
74
80
|
localUser: state.user.localUser,
|
|
75
81
|
});
|
|
76
82
|
|