homeflowjs 1.0.68 → 1.0.70
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,50 @@
|
|
1
|
+
import { useEffect, useState } from 'react';
|
2
|
+
import { isFunction } from '../utils';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Custom hook to observe the intersection of a target element with the viewport.
|
6
|
+
*
|
7
|
+
* @param {Object} params - Parameters for the hook.
|
8
|
+
* @param {HTMLElement|React.RefObject} params.target - The target element or a React ref to observe.
|
9
|
+
* @param {IntersectionObserverInit} [params.options={}] - Options for the IntersectionObserver (e.g., root, rootMargin, threshold).
|
10
|
+
* @param {Function} [params.onIntersect] - Callback function to execute when the target element intersects with the viewport.
|
11
|
+
*
|
12
|
+
* @returns {boolean} - A boolean indicating whether the target element is currently intersecting with the viewport.
|
13
|
+
*/
|
14
|
+
const useIntersectionObserver = ({ target, options = {}, onIntersect }) => {
|
15
|
+
const [isIntersecting, setIsIntersecting] = useState(false);
|
16
|
+
|
17
|
+
useEffect(() => {
|
18
|
+
if (!target) return;
|
19
|
+
|
20
|
+
let targetElement = target;
|
21
|
+
// If `target` is a ref, get the current DOM element
|
22
|
+
if (target?.current) {
|
23
|
+
targetElement = target.current;
|
24
|
+
}
|
25
|
+
|
26
|
+
const observer = new IntersectionObserver(
|
27
|
+
([entry]) => {
|
28
|
+
const intersecting = entry.isIntersecting;
|
29
|
+
setIsIntersecting(intersecting);
|
30
|
+
|
31
|
+
// Call the callback if provided and isIntersecting is true
|
32
|
+
if (intersecting && isFunction(onIntersect)) {
|
33
|
+
onIntersect();
|
34
|
+
}
|
35
|
+
},
|
36
|
+
options
|
37
|
+
);
|
38
|
+
|
39
|
+
observer.observe(targetElement);
|
40
|
+
|
41
|
+
// Cleanup observer on component unmount
|
42
|
+
return () => {
|
43
|
+
observer.disconnect();
|
44
|
+
};
|
45
|
+
}, [target, options, onIntersect]);
|
46
|
+
|
47
|
+
return isIntersecting;
|
48
|
+
};
|
49
|
+
|
50
|
+
export default useIntersectionObserver;
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import { useEffect } from 'react';
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
2
2
|
import { isFunction } from '../utils';
|
3
|
+
import useIntersectionObserver from './use-intersection-observer';;
|
3
4
|
|
4
|
-
const
|
5
|
-
|
5
|
+
const getTargetElementHelper = (target) => {
|
6
6
|
// If target is chunk ID
|
7
7
|
let targetElement;
|
8
8
|
if (!isNaN(target)) {
|
@@ -15,29 +15,38 @@ const scrollToTarget = (target, { yOffset = 0, additionalYOffset = 0 } = {}) =>
|
|
15
15
|
if (!targetElement) targetElement = document.querySelector(`.${target}`);
|
16
16
|
if (!targetElement) targetElement = document.querySelector(`#${target}`);
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
if (typeof yOffset === 'number') {
|
21
|
-
verticalOffset = yOffset;
|
22
|
-
} else if (isFunction(yOffset)) {
|
23
|
-
const value = yOffset();
|
24
|
-
if (typeof value === 'number') {
|
25
|
-
verticalOffset = value;
|
26
|
-
}
|
27
|
-
}
|
28
|
-
if(additionalYOffset) {
|
29
|
-
verticalOffset = verticalOffset + parseInt(additionalYOffset, 10);
|
30
|
-
}
|
18
|
+
return targetElement;
|
19
|
+
}
|
31
20
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
21
|
+
const scrollToTarget = ({ targetElement, yOffset = 0, additionalYOffset = 0 } = {}, signal) => {
|
22
|
+
if(!targetElement) return;
|
23
|
+
|
24
|
+
let verticalOffset = 0;
|
25
|
+
if (typeof yOffset === 'number') {
|
26
|
+
verticalOffset = yOffset;
|
27
|
+
} else if (isFunction(yOffset)) {
|
28
|
+
const value = yOffset();
|
29
|
+
if (typeof value === 'number') {
|
30
|
+
verticalOffset = value;
|
31
|
+
}
|
40
32
|
}
|
33
|
+
if(additionalYOffset) {
|
34
|
+
verticalOffset = verticalOffset + parseInt(additionalYOffset, 10);
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Scroll to the target element
|
39
|
+
* Note: when using getBoundingClientRect(), the position of the div will
|
40
|
+
* change whenever you scroll on the page. To get the absolute position of
|
41
|
+
* the element on the page, not relative to the window,we need to add the
|
42
|
+
* number of scrolled pixels to the element’s window location using scrollY:
|
43
|
+
*/
|
44
|
+
const rect = targetElement.getBoundingClientRect();
|
45
|
+
window.scroll({
|
46
|
+
top: rect.top + window.scrollY + verticalOffset,
|
47
|
+
behavior: 'smooth',
|
48
|
+
...(signal && { signal: signal }), // This is used to abort the scroll via a trigger in the useIntersectionObserver
|
49
|
+
});
|
41
50
|
};
|
42
51
|
|
43
52
|
const getURLParams = (hash) => {
|
@@ -46,37 +55,111 @@ const getURLParams = (hash) => {
|
|
46
55
|
return { target, yOffsetFromURLParams: yOffset };
|
47
56
|
};
|
48
57
|
|
49
|
-
function handleScrollToClick(options) {
|
50
|
-
// eslint-disable-next-line func-names
|
51
|
-
return function () {
|
52
|
-
const { target, yOffsetFromURLParams } = getURLParams(this.hash);
|
53
|
-
if (target) {
|
54
|
-
scrollToTarget(target, {
|
55
|
-
...options,
|
56
|
-
additionalYOffset: yOffsetFromURLParams,
|
57
|
-
});
|
58
|
-
}
|
59
|
-
};
|
60
|
-
}
|
61
58
|
|
59
|
+
/**
|
60
|
+
* Custom hook that provides functionality to scroll to a specific target element
|
61
|
+
* on the page, either based on URL hash parameters or click events to links with
|
62
|
+
* href="#scroll-to?target=". It also handles layout shifts caused by lazy-loaded components
|
63
|
+
* using an intersection observer to ensure accurate scrolling.
|
64
|
+
*
|
65
|
+
* Features:
|
66
|
+
* - Scrolls to a target element specified by a URL hash or click event.
|
67
|
+
* - Supports additional vertical offsets (static or dynamic).
|
68
|
+
* - Uses an AbortController to manage and stop scrolling when necessary.
|
69
|
+
* - Resolves layout shift issues by re-calculating the target position using an intersection observer.
|
70
|
+
* - Automatically attaches click event listeners to links with href="#scroll-to?target=",
|
71
|
+
* target can either be a element id, class name or chunk id.
|
72
|
+
* - Handles URL navigation with scroll-to hash parameters.
|
73
|
+
*
|
74
|
+
* @param {Object} options - Configuration options for scrolling behavior.
|
75
|
+
* @param {number|function} [options.yOffset] - Vertical offset for scrolling. Can be a number or a function.
|
76
|
+
* @param {number} [options.additionalYOffset] - Additional vertical offset for scrolling.
|
77
|
+
*/
|
62
78
|
const useScrollTo = (options) => {
|
79
|
+
const [currentTarget, setCurrentTarget] = useState(null);
|
80
|
+
const scrollAbortControllerRef = useRef(null);
|
81
|
+
|
82
|
+
const handleScrollTo = () => {
|
83
|
+
const { target, yOffsetFromURLParams } = getURLParams(window.location.hash);
|
84
|
+
const targetElement = getTargetElementHelper(target);
|
85
|
+
|
86
|
+
if(!targetElement) return;
|
87
|
+
|
88
|
+
const targetObj = {
|
89
|
+
targetElement,
|
90
|
+
additionalYOffset: yOffsetFromURLParams,
|
91
|
+
...options,
|
92
|
+
}
|
93
|
+
|
94
|
+
// Update state so that the intersection observer can pick it up
|
95
|
+
setCurrentTarget(targetObj);
|
96
|
+
|
97
|
+
// AbortController to manage the scroll
|
98
|
+
const controller = new AbortController();
|
99
|
+
scrollAbortControllerRef.current = controller;
|
100
|
+
|
101
|
+
// Scroll to the target element;
|
102
|
+
scrollToTarget({ ...targetObj }, controller.signal);
|
103
|
+
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* How this useIntersectionObserver is being used:
|
107
|
+
* This fixes a bug with the scrollToTarget function
|
108
|
+
* where the getBoundingClientRect() method returns
|
109
|
+
* a different value between scroll to's. This is caused
|
110
|
+
* because of layout shift with lazy loaded components
|
111
|
+
* while scrolling.
|
112
|
+
*
|
113
|
+
* How it works:
|
114
|
+
* When the scrollToTarget is fired, the useIntersectionObserver
|
115
|
+
* will check if the target element is in the viewport. If it is
|
116
|
+
* it stops the window.scroll method in the scrollToTarget function
|
117
|
+
* stopping the scroll. This is done by using the AbortController.
|
118
|
+
* Then it scrollToTarget again, this in turn gets the most up to date
|
119
|
+
* getBoundingClientRect() value and scrolls to the target element.
|
120
|
+
*
|
121
|
+
*/
|
122
|
+
useIntersectionObserver({
|
123
|
+
target: currentTarget?.targetElement,
|
124
|
+
onIntersect: () => {
|
125
|
+
if (scrollAbortControllerRef.current) {
|
126
|
+
scrollAbortControllerRef.current.abort(); // Stop the scroll
|
127
|
+
scrollAbortControllerRef.current = null; // Reset the controller
|
128
|
+
scrollToTarget({ ...currentTarget }); // Fire the scrollToTarget function again
|
129
|
+
}
|
130
|
+
}
|
131
|
+
});
|
132
|
+
|
133
|
+
|
134
|
+
// Handle onclick scroll to events
|
63
135
|
useEffect(() => {
|
136
|
+
const clickEventController = new AbortController()
|
64
137
|
const scrollToEls = document.querySelectorAll(
|
65
138
|
'[href*="#scroll-to"], [href*="#/scroll-to"]'
|
66
139
|
);
|
140
|
+
|
67
141
|
scrollToEls.forEach((el) => {
|
68
142
|
el.addEventListener(
|
69
143
|
'click',
|
70
|
-
|
144
|
+
(e) => {
|
145
|
+
e.preventDefault();
|
146
|
+
handleScrollTo();
|
147
|
+
},
|
148
|
+
{ signal: clickEventController.signal }
|
71
149
|
);
|
72
150
|
});
|
73
151
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
});
|
152
|
+
//On click element event listeners cleanup
|
153
|
+
return () => {
|
154
|
+
clickEventController.abort()
|
155
|
+
}
|
79
156
|
}, []);
|
157
|
+
|
158
|
+
// Handle when a user navigates to a URL with a scroll to hash
|
159
|
+
useEffect(() => {
|
160
|
+
handleScrollTo();
|
161
|
+
}, [])
|
162
|
+
|
80
163
|
};
|
81
164
|
|
82
165
|
export default useScrollTo;
|
package/package.json
CHANGED
@@ -4,65 +4,61 @@ import englishRates from '../../utils/stamp-duty-calculator/english-rates';
|
|
4
4
|
|
5
5
|
import './stamp-duty-calculator.styles.scss';
|
6
6
|
|
7
|
-
const DefaultCalculator = ({
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
const
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
7
|
+
const DefaultCalculator = ({
|
8
|
+
purchasePrice,
|
9
|
+
setPurchasePrice,
|
10
|
+
firstTimeBuyer,
|
11
|
+
additionalProperty,
|
12
|
+
ukResident,
|
13
|
+
buyToLet
|
14
|
+
}) => {
|
15
|
+
const [priceDisplay, setPriceDisplay] = useState(purchasePrice);
|
16
|
+
const results = englishRates(
|
17
|
+
purchasePrice,
|
18
|
+
(firstTimeBuyer && !buyToLet),
|
19
|
+
(additionalProperty || buyToLet),
|
20
|
+
ukResident,
|
21
|
+
);
|
22
|
+
|
23
|
+
const englandRatesList = Homeflow.get('stamp_duty_england');
|
24
|
+
|
25
|
+
const ratesToCalculateBasedOnTaxType = () => {
|
26
|
+
const baseRates = englandRatesList['base'];
|
27
|
+
const firstTimeBuyerRates = englandRatesList['first_time_buyer'];
|
28
|
+
|
29
|
+
if (firstTimeBuyer && !buyToLet) {
|
30
|
+
const priceLimit = firstTimeBuyerRates[firstTimeBuyerRates?.length - 1]
|
31
|
+
?.['maximum_property_price'];
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
rate5,
|
39
|
-
effectiveRate,
|
40
|
-
totalRate
|
41
|
-
} = results;
|
42
|
-
|
43
|
-
setRate1(rate1);
|
44
|
-
setRate2(rate2);
|
45
|
-
setRate3(rate3);
|
46
|
-
setRate4(rate4);
|
47
|
-
setRate5(rate5);
|
48
|
-
setTotalRate(totalRate);
|
49
|
-
setEffectiveRate(Math.round(effectiveRate * 10) / 10);
|
50
|
-
|
51
|
-
return null;
|
52
|
-
};
|
53
|
-
|
54
|
-
useEffect(() => {
|
55
|
-
if (property && property.price_value) {
|
56
|
-
setPurchasePrice(property.price_value);
|
57
|
-
calculateStampDuty(property.price_value);
|
33
|
+
if (priceLimit && purchasePrice > priceLimit) {
|
34
|
+
return baseRates;
|
35
|
+
} else {
|
36
|
+
return firstTimeBuyerRates;
|
37
|
+
}
|
58
38
|
}
|
59
|
-
}, [firstTimeBuyer, additionalProperty]);
|
60
39
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
40
|
+
return baseRates;
|
41
|
+
}
|
42
|
+
|
43
|
+
const ratesList = ratesToCalculateBasedOnTaxType();
|
44
|
+
|
45
|
+
const handleInputUpdate = (val) => {
|
46
|
+
let transformValues = val;
|
47
|
+
|
48
|
+
// Only accept numbers
|
49
|
+
if(transformValues.toUpperCase() != transformValues.toLowerCase()) return;
|
50
|
+
|
51
|
+
// If errors, default to 0
|
52
|
+
if (transformValues === NaN) transformValues = 0;
|
53
|
+
|
54
|
+
// Hacky way to keep the £ and multiple , in the input box while ultimately passing a number
|
55
|
+
if (transformValues?.includes('£')) transformValues = transformValues.replace('£', '');
|
56
|
+
if (transformValues?.includes(',')) transformValues = transformValues.replaceAll(',', '');
|
57
|
+
|
58
|
+
transformValues = +transformValues;
|
59
|
+
|
60
|
+
setPriceDisplay(transformValues);
|
61
|
+
}
|
66
62
|
|
67
63
|
return (
|
68
64
|
<div id="stampResi" className="stamp-duty-calculator main">
|
@@ -72,8 +68,8 @@ const DefaultCalculator = ({ purchasePrice, setPurchasePrice, firstTimeBuyer, ad
|
|
72
68
|
className="SD_money residential auto"
|
73
69
|
type="text"
|
74
70
|
placeholder="Purchase price"
|
75
|
-
value={`£${
|
76
|
-
onChange={
|
71
|
+
value={`£${priceDisplay?.toLocaleString() || '0'}`}
|
72
|
+
onChange={e => handleInputUpdate(e.target.value)}
|
77
73
|
/>
|
78
74
|
</div>
|
79
75
|
|
@@ -82,7 +78,7 @@ const DefaultCalculator = ({ purchasePrice, setPurchasePrice, firstTimeBuyer, ad
|
|
82
78
|
type="button"
|
83
79
|
className="SD_calculate btn residential"
|
84
80
|
data-preselector=".main"
|
85
|
-
onClick={() =>
|
81
|
+
onClick={() => setPurchasePrice(priceDisplay)}
|
86
82
|
>
|
87
83
|
Calculate
|
88
84
|
</button>
|
@@ -95,135 +91,65 @@ const DefaultCalculator = ({ purchasePrice, setPurchasePrice, firstTimeBuyer, ad
|
|
95
91
|
{' '}
|
96
92
|
<span className="SD_result residential">
|
97
93
|
£
|
98
|
-
{Number(totalRate).toLocaleString()}
|
94
|
+
{Number(results?.totalRate || 0).toLocaleString()}
|
99
95
|
</span>
|
100
96
|
{' '}
|
101
97
|
Stamp duty
|
102
98
|
</p>
|
103
99
|
|
104
|
-
{(firstTimeBuyer && property.price_value >= 625000) && (
|
105
|
-
<p id="first_time_buyer_info">First time buyers purchasing property for more than £625,000 will not be entitled to any relief and will pay SDLT at the standard rates.</p>
|
106
|
-
)}
|
107
|
-
|
108
100
|
<div className="results-table-wrap">
|
109
101
|
<p><strong>How this was calculated:</strong></p>
|
110
|
-
|
111
|
-
<table className="SD_results_table residential">
|
102
|
+
<table className="SD_results_table residential">
|
112
103
|
<thead>
|
113
104
|
<tr>
|
114
105
|
<th>Band</th>
|
115
|
-
<th>Stamp Duty Rate</th>
|
106
|
+
<th>{`${!buyToLet ? 'Stamp Duty ' : ''}Rate`}</th>
|
107
|
+
{(additionalProperty && !firstTimeBuyer && !buyToLet) && (
|
108
|
+
<th>Additional Property Rate</th>
|
109
|
+
)}
|
116
110
|
<th>Due</th>
|
117
111
|
</tr>
|
118
112
|
</thead>
|
119
113
|
<tbody>
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
114
|
+
{ratesList?.map((rate, index) => {
|
115
|
+
// long string builder
|
116
|
+
let titleForRate = `${rate?.maximum_property_price ? 'Between' : 'Over'}`;
|
117
|
+
titleForRate += ' ';
|
118
|
+
titleForRate += `£${(+rate?.minimum_property_price).toLocaleString()}`;
|
119
|
+
titleForRate += `${rate?.maximum_property_price ? ' and £' : ''}`;
|
120
|
+
titleForRate += `${(+rate?.maximum_property_price).toLocaleString() || ''}`;
|
121
|
+
|
122
|
+
return (
|
123
|
+
<tr key={rate.id}>
|
124
|
+
<td>
|
125
|
+
{titleForRate}
|
126
|
+
</td>
|
127
|
+
{buyToLet ? (
|
128
|
+
<td>
|
129
|
+
{`${(+rate?.base_sdlt_rate || 0) + (+rate?.additional_property_surcharge || 0)}%`}
|
130
|
+
</td>
|
131
|
+
) : (
|
132
|
+
<td>
|
133
|
+
{`${rate?.base_sdlt_rate || 0}%`}
|
134
|
+
</td>
|
135
|
+
)}
|
136
|
+
{(additionalProperty && !firstTimeBuyer && !buyToLet) && (
|
137
|
+
<td className="additional_rate">
|
138
|
+
{`${+rate?.additional_property_surcharge}%`}
|
139
|
+
</td>
|
140
|
+
)}
|
141
|
+
<td className="rate1">
|
142
|
+
{`£${Number(results?.[`rate${index + 1}`]).toLocaleString()}`}
|
143
|
+
</td>
|
144
|
+
</tr>
|
145
|
+
);
|
146
|
+
})}
|
147
147
|
</tbody>
|
148
148
|
</table>
|
149
|
-
) : (
|
150
|
-
<table className="SD_results_table residential">
|
151
|
-
<thead>
|
152
|
-
<tr>
|
153
|
-
<th>Band</th>
|
154
|
-
<th>Stamp Duty Rate</th>
|
155
|
-
<th>Additional Property Rate</th>
|
156
|
-
<th>Due</th>
|
157
|
-
</tr>
|
158
|
-
</thead>
|
159
|
-
<tbody>
|
160
|
-
<tr>
|
161
|
-
<td>Between £0 and £250,000</td>
|
162
|
-
<td> 0% </td>
|
163
|
-
<td className='additional_rate'> 3% </td>
|
164
|
-
<td className="rate1">
|
165
|
-
£
|
166
|
-
{Number(rate1).toLocaleString()}
|
167
|
-
</td>
|
168
|
-
</tr>
|
169
|
-
{/* <tr>
|
170
|
-
<td>Between £125,000 and £250,000</td>
|
171
|
-
<td> 2% </td>
|
172
|
-
<td className="rate2">
|
173
|
-
£
|
174
|
-
{Number(rate2).toLocaleString()}
|
175
|
-
</td>
|
176
|
-
</tr> */}
|
177
|
-
<tr>
|
178
|
-
<td>Between £250,001 and £925,000</td>
|
179
|
-
<td> 5% </td>
|
180
|
-
<td className='additional_rate'> 8% </td>
|
181
|
-
<td className="rate3">
|
182
|
-
£
|
183
|
-
{Number(rate2).toLocaleString()}
|
184
|
-
</td>
|
185
|
-
</tr>
|
186
|
-
<tr>
|
187
|
-
<td>Between £925,000 and £1,500,000 </td>
|
188
|
-
<td> 10% </td>
|
189
|
-
<td className='additional_rate'> 13% </td>
|
190
|
-
<td className="rate4">
|
191
|
-
£
|
192
|
-
{Number(rate3).toLocaleString()}
|
193
|
-
</td>
|
194
|
-
</tr>
|
195
|
-
<tr>
|
196
|
-
<td>Over £1,500,000 </td>
|
197
|
-
<td> 12% </td>
|
198
|
-
<td className='additional_rate'> 15% </td>
|
199
|
-
<td className="rate5">
|
200
|
-
£
|
201
|
-
{Number(rate4).toLocaleString()}
|
202
|
-
</td>
|
203
|
-
</tr>
|
204
|
-
<tr className="totals">
|
205
|
-
<td>Total</td>
|
206
|
-
<td className="effectiveRate">
|
207
|
-
{effectiveRate}
|
208
|
-
%
|
209
|
-
</td>
|
210
|
-
<td></td>
|
211
|
-
<td className="totalRate">
|
212
|
-
£
|
213
|
-
{Number(totalRate).toLocaleString()}
|
214
|
-
</td>
|
215
|
-
</tr>
|
216
|
-
</tbody>
|
217
|
-
</table>
|
218
|
-
)}
|
219
149
|
</div>
|
220
150
|
</div>
|
221
151
|
</div>
|
222
152
|
);
|
223
153
|
};
|
224
154
|
|
225
|
-
|
226
|
-
themePreferences: state.app.themePreferences,
|
227
|
-
});
|
228
|
-
|
229
|
-
export default connect(mapStateToProps)(DefaultCalculator);
|
155
|
+
export default DefaultCalculator;
|
@@ -1,14 +1,16 @@
|
|
1
1
|
import React, { useState } from 'react';
|
2
|
-
import { connect } from 'react-redux';
|
3
2
|
import DefaultCalculator from './default-calculator.component';
|
4
|
-
import BTLCalculator from './btl-calculator.component';
|
5
3
|
|
6
4
|
import './stamp-duty-calculator.styles.scss';
|
7
5
|
|
8
|
-
const StampDutyCalculator = (
|
6
|
+
const StampDutyCalculator = () => {
|
7
|
+
const property = Homeflow.get('property');
|
9
8
|
const [type, setType] = useState('residential');
|
9
|
+
const placeholderValue = 250_000;
|
10
|
+
const [purchasePrice, setPurchasePrice] = useState(
|
11
|
+
(property && property?.price_value) ? property.price_value : placeholderValue
|
12
|
+
);
|
10
13
|
const [purchaseType, setPurchaseType] = useState('firstTimeBuyer')
|
11
|
-
const [purchasePrice, setPurchasePrice] = useState();
|
12
14
|
const [firstTimeBuyer, setFirstTimeBuyer] = useState(false);
|
13
15
|
const [additionalProperty, setAdditionalProperty] = useState(false);
|
14
16
|
|
@@ -21,6 +23,7 @@ const StampDutyCalculator = ({ scotland, themePreferences }) => {
|
|
21
23
|
firstTimeBuyer={firstTimeBuyer}
|
22
24
|
additionalProperty={additionalProperty}
|
23
25
|
ukResident
|
26
|
+
buyToLet={type === 'btl'}
|
24
27
|
/>
|
25
28
|
);
|
26
29
|
}
|
@@ -55,7 +58,11 @@ const StampDutyCalculator = ({ scotland, themePreferences }) => {
|
|
55
58
|
<button
|
56
59
|
type="button"
|
57
60
|
className={`stamp-duty-cal__type ${type === 'btl' ? 'selected' : ''}`}
|
58
|
-
onClick={() =>
|
61
|
+
onClick={() => {
|
62
|
+
if (firstTimeBuyer) setFirstTimeBuyer(prev => !prev);
|
63
|
+
if (additionalProperty) setAdditionalProperty(prev => !prev);
|
64
|
+
setType('btl');
|
65
|
+
}}
|
59
66
|
>
|
60
67
|
Buy to Let
|
61
68
|
</button>
|
@@ -81,13 +88,16 @@ const StampDutyCalculator = ({ scotland, themePreferences }) => {
|
|
81
88
|
<label htmlFor="additionalProperty">Additional Property</label>
|
82
89
|
</div>
|
83
90
|
</div>
|
84
|
-
|
91
|
+
<DefaultCalculator
|
92
|
+
purchasePrice={purchasePrice}
|
93
|
+
setPurchasePrice={setPurchasePrice}
|
94
|
+
firstTimeBuyer={firstTimeBuyer}
|
95
|
+
additionalProperty={additionalProperty}
|
96
|
+
ukResident
|
97
|
+
buyToLet={type === 'btl'}
|
98
|
+
/>
|
85
99
|
</div>
|
86
100
|
);
|
87
101
|
};
|
88
102
|
|
89
|
-
|
90
|
-
themePreferences: state.app.themePreferences,
|
91
|
-
});
|
92
|
-
|
93
|
-
export default connect(mapStateToProps)(StampDutyCalculator);
|
103
|
+
export default StampDutyCalculator;
|