homeflowjs 1.0.60 → 1.0.62

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homeflowjs",
3
- "version": "1.0.60",
3
+ "version": "1.0.62",
4
4
  "sideEffects": [
5
5
  "modal/**/*",
6
6
  "user/default-profile/**/*",
@@ -8,12 +8,6 @@ import { addSearchToLocalStorage } from '../../app/user-history';
8
8
  import { Link } from 'react-router-dom';
9
9
 
10
10
  const LoadMoreButton = (props) => {
11
- const dispatch = useDispatch();
12
- const currentSearch = useSelector((state) => state.search?.currentSearch);
13
- const pagination = useSelector((state) => state.properties?.pagination);
14
-
15
- if (!pagination.has_next_page) return null;
16
-
17
11
  const {
18
12
  isRouterLink,
19
13
  children,
@@ -21,21 +15,38 @@ const LoadMoreButton = (props) => {
21
15
  isHidden,
22
16
  ...otherProps
23
17
  } = props;
24
-
18
+ const dispatch = useDispatch();
19
+ const pagination = useSelector((state) => state.properties?.pagination);
20
+ const currentSearch = useSelector((state) => state.search?.currentSearch);
25
21
  const [loading, setLoading] = useState(false);
26
22
  const [targetURL, setTargetURL] = useState(window.location.href);
27
23
 
28
- // Updating the url for the link to follow the next page,
29
- // replacing the page with current page with the next page value
30
- // to avoid duplicated pages in url pattern such as page-2/page-3.
31
- const updatePageLink = (url) => {
24
+ /**
25
+ * There might be a slight delay between the pagination and the properties
26
+ * update in the redux store and the execution of the dispatch.then()
27
+ * that can affect the page number link. This is hacks that possible delay.
28
+ */
29
+ const updateLinkUrlWithDelay = () => setTimeout(
30
+ () => setTargetURL(window.location.href), 250,
31
+ );
32
+
33
+ /**
34
+ * Updating the url for the link to follow the next page,
35
+ * replacing the page with current page with the next page value
36
+ * to avoid duplicated pages in url pattern such as page-2/page-3.
37
+ */
38
+ const updatePageLink = (pageUrl) => {
39
+ const url = pageUrl.replace('#/', '');
40
+
32
41
  if (url.includes('page-')) {
33
42
  return url.replace(/page-\d+/, `page-${pagination.current_page + 1}`);
34
43
  } else {
35
44
  // Fix for the page-2 when the url does not have a value for page-1
36
- return url + (url.endsWith('/') ? '' : '/') + `page-${pagination.current_page + 1}`;
45
+ return url
46
+ + (url.endsWith('/') ? '' : '/')
47
+ + (pagination.has_next_page ? `page-${pagination.current_page + 1}` : '');
37
48
  }
38
- }
49
+ };
39
50
 
40
51
  const handleClick = (e) => {
41
52
  e.preventDefault();
@@ -49,14 +60,20 @@ const LoadMoreButton = (props) => {
49
60
 
50
61
  setLoading(false);
51
62
  addSearchToLocalStorage(searchToLocal);
52
- setTargetURL(window.location.href);
63
+ updateLinkUrlWithDelay();
53
64
  return false;
54
65
  });
55
66
  return false;
56
67
  }
57
68
 
58
- {/* Load More Button component uses Link from react-router-dom to avoid navigating pages for themes using the component inside a react router wrapper. */}
69
+ /*
70
+ * Load More Button component uses Link from react-router-dom to
71
+ * avoid navigating pages for themes using the component inside
72
+ * a react router wrapper.
73
+ */
59
74
  const RouterLinkComponent = () => {
75
+ if (!pagination.has_next_page) return null;
76
+
60
77
  return (
61
78
  <Link
62
79
  onClick={(e) => handleClick(e)}
@@ -69,6 +86,8 @@ const LoadMoreButton = (props) => {
69
86
  }
70
87
 
71
88
  const LinkComponent = () => {
89
+ if (!pagination.has_next_page) return null;
90
+
72
91
  return (
73
92
  <a
74
93
  onClick={(e) => handleClick(e)}
@@ -80,8 +99,14 @@ const LoadMoreButton = (props) => {
80
99
  )
81
100
  };
82
101
 
83
- {/* Hidden link for themes using InfiniteScroll: allows Googlebot to discover the Next page link for indexing, while users trigger infinite scroll without actual navigation. */}
102
+ /*
103
+ * Hidden link for themes using InfiniteScroll: allows Googlebot to discover
104
+ * the Next page link for indexing, while users trigger infinite scroll
105
+ * without actual navigation.
106
+ */
84
107
  const HiddenLinkComponent = () => {
108
+ if (!pagination.has_next_page) return null;
109
+
85
110
  return (
86
111
  <a
87
112
  style={{ display: 'none' }}
@@ -0,0 +1,53 @@
1
+ import { calculateRange, getPriceBracketIndex } from "./stamp-duty-utils";
2
+
3
+ const countryRates = (price, firstTimeBuyer, additionalProperty, ukResident, country) => {
4
+ const countryConfig = {
5
+ 'scotland': {
6
+ cmsData: Homeflow.get('stamp_duty_scotland'),
7
+ base: 'base_lbtt_rate',
8
+ },
9
+ 'wales': {
10
+ cmsData: Homeflow.get('stamp_duty_wales'),
11
+ base: 'base_ltt_rate',
12
+ }
13
+ }
14
+ const cmsRates = countryConfig[country].cmsData;
15
+
16
+ const buyerStatusAsKeyOfObject = () => {
17
+ if(firstTimeBuyer) return 'first_time_buyer_rate';
18
+ if(additionalProperty) return 'additional_property_rate';
19
+ return countryConfig[country].base;
20
+ };
21
+ const bracketForPrice = +(cmsRates[getPriceBracketIndex(price, cmsRates)]['non_uk_resident_rate']);
22
+
23
+ const initalTotalRate = () => {
24
+ if (!ukResident && bracketForPrice) return +((price * (bracketForPrice / 100)).toFixed(2));
25
+ return 0;
26
+ }
27
+
28
+ const stampDutyRatesCalculation = cmsRates.reduce((accumulator, _, index) => {
29
+ const percentageFromList = +(cmsRates[index][buyerStatusAsKeyOfObject()]) / 100;
30
+ const minimumPropertyPrice = cmsRates[index]['minimum_property_price'] || Number.POSITIVE_INFINITY;
31
+ const maxisumPropertyPrice = cmsRates[index]['maximum_property_price'] || Number.POSITIVE_INFINITY;
32
+ const rateIndex = `rate${index + 1}`;
33
+ const rateForTheBracket = calculateRange(price, minimumPropertyPrice, maxisumPropertyPrice, percentageFromList);
34
+
35
+ accumulator[rateIndex] = rateForTheBracket;
36
+ accumulator.totalRate += rateForTheBracket;
37
+
38
+ accumulator.effectiveRate = (accumulator.totalRate / price) * 100;
39
+
40
+ return accumulator;
41
+ }, {
42
+ totalRate: initalTotalRate(),
43
+ effectiveRate: 0,
44
+ });
45
+
46
+ // Roundings
47
+ stampDutyRatesCalculation.effectiveRate = Math.round(stampDutyRatesCalculation.effectiveRate * 10) / 10;
48
+ stampDutyRatesCalculation.totalRate = Math.round(stampDutyRatesCalculation.totalRate);
49
+
50
+ return stampDutyRatesCalculation;
51
+ };
52
+
53
+ export default countryRates;
@@ -1,79 +1,55 @@
1
- const calculateRange = (price, lower, upper, rate) => {
2
- if (price <= lower) return 0;
3
- return (Math.min(upper, price) - lower) * rate;
4
- };
1
+ import { calculateRange, getPriceBracketIndex } from "./stamp-duty-utils";
5
2
 
6
3
  const englishRates = (
7
- price,
4
+ price = 0,
8
5
  firstTimeBuyer,
9
6
  additionalProperty,
10
7
  ukResident = true, // Default to true if not explicitly specified
11
8
  isCompany
12
9
  ) => {
13
- // Handle cases where price is missing or invalid
14
- if (!price || price <= 0) {
15
- return {
16
- rate1: 0,
17
- rate2: 0,
18
- rate3: 0,
19
- rate4: 0,
20
- rate5: 0,
21
- totalRate: 0,
22
- effectiveRate: 0,
23
- };
24
- }
10
+ const englandRatesList = Homeflow.get('stamp_duty_england');
11
+ const bracketForPrice = +(englandRatesList[getPriceBracketIndex(price, englandRatesList)]['non_uk_resident_rate']);
25
12
 
26
- let effectiveRate = 0;
27
- let totalRate = 0;
28
- let rate1 = 0;
29
- let rate2 = 0;
30
- let rate3 = 0;
31
- let rate4 = 0;
32
- let rate5 = 0;
13
+ const buyerStatusAsKeyOfObject = () => {
14
+ if(firstTimeBuyer) return 'first_time_buyer_rate';
15
+ if(additionalProperty) return 'additional_property_rate';
16
+ return 'base_sdlt_rate';
17
+ };
33
18
 
34
- if (isCompany) {
35
- // Company buy: Flat rate of 17% for properties above £500,000
36
- if (price > 500000) {
37
- totalRate = price * 0.17;
38
- }
39
- } else {
40
- // Individual buyers
41
- if (firstTimeBuyer && price <= 625000) {
42
- rate2 = calculateRange(price, 425000, Number.POSITIVE_INFINITY, 0.05);
43
- }
19
+ /**
20
+ * ATM the BE work doesn't have % for company purchases.
21
+ * Once that's done we can initiate the base object totalRate for:
22
+ * totalRate: (!ukResident && bracketForPrice) ? +((price * (bracketForPrice / 100)).toFixed(2)) : 0,
23
+ * But for now we need to have the 3 options + the hardcoded 17%
24
+ */
25
+ const initalTotalRate = () => {
26
+ if(isCompany && price > 500_000) return price * 0.17;
27
+ if (!ukResident && bracketForPrice && !isCompany) return +((price * (bracketForPrice / 100)).toFixed(2));
28
+ return 0;
29
+ }
44
30
 
45
- if (additionalProperty) {
46
- // Additional property rates
47
- rate1 = calculateRange(price, 0, 250000, 0.05);
48
- rate2 = calculateRange(price, 250000, 925000, 0.10);
49
- rate3 = calculateRange(price, 925000, 1500000, 0.15);
50
- rate4 = calculateRange(price, 1500000, Number.POSITIVE_INFINITY, 0.17);
51
- } else if (!(firstTimeBuyer && price <= 625000)) {
52
- // Moving or first-time buyer > £625,000
53
- rate2 = calculateRange(price, 250000, 925000, 0.05);
54
- rate3 = calculateRange(price, 925000, 1500000, 0.10);
55
- rate4 = calculateRange(price, 1500000, Number.POSITIVE_INFINITY, 0.12);
56
- }
31
+ const stampDutyRatesCalculation = englandRatesList.reduce((accumulator, _, index) => {
32
+ const percentageFromList = +(englandRatesList[index][buyerStatusAsKeyOfObject()]) / 100;
33
+ const minimumPropertyPrice = englandRatesList[index]['minimum_property_price'] || Number.POSITIVE_INFINITY;
34
+ const maxisumPropertyPrice = englandRatesList[index]['maximum_property_price'] || Number.POSITIVE_INFINITY;
35
+ const rateIndex = `rate${index + 1}`;
36
+ const rateForTheBracket = isCompany
37
+ ? 0
38
+ : calculateRange(price, minimumPropertyPrice, maxisumPropertyPrice, percentageFromList);
57
39
 
58
- totalRate = rate1 + rate2 + rate3 + rate4 + rate5;
40
+ accumulator[rateIndex] = rateForTheBracket;
41
+ accumulator.totalRate += rateForTheBracket;
59
42
 
60
- // Apply additional rate for non-UK residents only if applicable
61
- if (!ukResident) {
62
- totalRate += price * 0.02; // Non-UK resident surcharge
63
- }
64
- }
43
+ accumulator.effectiveRate = (accumulator.totalRate / price) * 100;
65
44
 
66
- effectiveRate = (totalRate / price) * 100;
45
+ return accumulator;
46
+ }, {
47
+ // totalRate: (!ukResident && bracketForPrice) ? +((price * (bracketForPrice / 100)).toFixed(2)) : 0,
48
+ totalRate: initalTotalRate(),
49
+ effectiveRate: 0,
50
+ });
67
51
 
68
- return {
69
- rate1,
70
- rate2,
71
- rate3,
72
- rate4,
73
- rate5,
74
- totalRate,
75
- effectiveRate,
76
- };
52
+ return stampDutyRatesCalculation;
77
53
  };
78
54
 
79
55
  export default englishRates;
@@ -1,6 +1,5 @@
1
1
  import englishRates from './english-rates';
2
- import scottishRates from './scottish-rates';
3
- import welshRates from './welsh-rates';
2
+ import countryRates from './country-rates';
4
3
 
5
4
  /**
6
5
  * We already have a DefaultStampDutyCalculator component,
@@ -18,14 +17,13 @@ const stampDutyCalculator = ({
18
17
  };
19
18
  }
20
19
 
21
- // Scotland
22
- if (country === 'scotland') {
23
- return scottishRates(price, firstTimeBuyer, additionalProperty, ukResident);
24
- }
25
-
26
- // Wales
27
- if (country === 'wales') {
28
- return welshRates(price, additionalProperty, ukResident);
20
+ /**
21
+ * For Scotland and Wales
22
+ * Once the BE brings data for companies (isCompany)
23
+ * countryRates will need an update and then we can have just one method.
24
+ */
25
+ if (country === 'scotland' || country === 'wales') {
26
+ return countryRates(price, firstTimeBuyer, additionalProperty, ukResident, country);
29
27
  }
30
28
 
31
29
  // If country not is not specified use English rates by default
@@ -0,0 +1,21 @@
1
+ export const calculateRange = (price, lower, upper, rate) => {
2
+ if (price <= lower) return 0;
3
+ return Math.round(((Math.min(upper, price) - lower) * rate) * 10) / 10;
4
+ };
5
+
6
+ export const getPriceBracketIndex = (price, arr) => {
7
+ const lastIndex = arr.length - 1;
8
+ let bracketIndex = null;
9
+
10
+ if (price <= arr[0].maximum_property_price) return 0;
11
+ if (price >= arr[lastIndex].minimum_property_price) return lastIndex;
12
+
13
+ arr.forEach((rates, index) => {
14
+ if (
15
+ (rates.minimum_property_price || 0) <= price
16
+ && (rates.minimum_property_price || Number.POSITIVE_INFINITY) > price
17
+ ) bracketIndex = index;
18
+ });
19
+
20
+ return bracketIndex || 0;
21
+ }
@@ -1,104 +0,0 @@
1
- const scottishRates = (price, firstTimeBuyer, additionalProperty, ukResident) => {
2
- let effectiveRate = 0;
3
- let totalRate = 0;
4
- const rate1 = 0;
5
- let rate2 = 0;
6
- let rate3 = 0;
7
- let rate4 = 0;
8
- let rate5 = 0;
9
-
10
- if (firstTimeBuyer) {
11
- // Rate 2
12
- if (price - 175000 > 0) {
13
- if ((price - 175000) > (250000 - 175000)) {
14
- rate2 = (250000 - 175000) * 0.02;
15
- } else {
16
- rate2 = (price - 175000) * 0.02;
17
- }
18
- }
19
-
20
- // Rate 3
21
- if (price - 250000 > 0) {
22
- if ((price - 250000) > (325000 - 250000)) {
23
- rate3 = (325000 - 250000) * 0.05;
24
- } else {
25
- rate3 = (price - 250000) * 0.05;
26
- }
27
- }
28
-
29
- // Rate 4
30
- if (price - 325000 > 0) {
31
- if ((price - 325000) > (750000 - 325000)) {
32
- rate4 = (750000 - 325000) * 0.10;
33
- } else {
34
- rate4 = (price - 325000) * 0.10;
35
- }
36
- }
37
-
38
- // Rate 5
39
- if (price - 750000 > 0) {
40
- rate5 = (price - 750000);
41
- rate5 *= 0.12;
42
- }
43
- }
44
-
45
- if (!firstTimeBuyer) {
46
- // Rate 2
47
- if (price - 145000 > 0) {
48
- if ((price - 145000) > (250000 - 145000)) {
49
- rate2 = (250000 - 145000) * 0.02;
50
- } else {
51
- rate2 = (price - 145000) * 0.02;
52
- }
53
- }
54
-
55
- // Rate 3
56
- if (price - 250000 > 0) {
57
- if ((price - 250000) > (325000 - 250000)) {
58
- rate3 = (325000 - 250000) * 0.05;
59
- } else {
60
- rate3 = (price - 250000) * 0.05;
61
- }
62
- }
63
-
64
- // Rate 4
65
- if (price - 325000 > 0) {
66
- if ((price - 325000) > (750000 - 325000)) {
67
- rate4 = (750000 - 325000) * 0.10;
68
- } else {
69
- rate4 = (price - 325000) * 0.10;
70
- }
71
- }
72
-
73
- // Rate 5
74
- if (price - 750000 > 0) {
75
- rate5 = (price - 750000);
76
- rate5 *= 0.12;
77
- }
78
- }
79
-
80
- totalRate = rate1 + rate2 + rate3 + rate4 + rate5;
81
- effectiveRate = totalRate / price;
82
- if (!ukResident) {
83
- totalRate = totalRate * (effectiveRate + 0.02) / effectiveRate;
84
- effectiveRate += 0.02;
85
- }
86
- effectiveRate *= 100;
87
-
88
- if (additionalProperty) {
89
- const ADS = price * 0.06;
90
- totalRate += ADS;
91
- }
92
-
93
- return {
94
- rate1,
95
- rate2,
96
- rate3,
97
- rate4,
98
- rate5,
99
- totalRate,
100
- effectiveRate: Math.round(effectiveRate * 10) / 10,
101
- };
102
- };
103
-
104
- export default scottishRates;
@@ -1,114 +0,0 @@
1
- const welshRates = (price, additionalProperty, ukResident) => {
2
- let effectiveRate = 0;
3
- let totalRate = 0;
4
- let rate1 = 0;
5
- let rate2 = 0;
6
- let rate3 = 0;
7
- let rate4 = 0;
8
- let rate5 = 0;
9
- let rate6 = 0;
10
-
11
- if (additionalProperty) {
12
- // Rate 1
13
- if (price > 180000) rate1 = 180000 * 0.05;
14
- if (price <= 180000) {
15
- rate1 = price * 0.05;
16
- }
17
-
18
- // Rate 2
19
- if (price - 180000 > 0) {
20
- if ((price - 180000) > (250000 - 180000)) {
21
- rate2 = (250000 - 180000) * 0.085;
22
- } else {
23
- rate2 = (price - 180000) * 0.085;
24
- }
25
- }
26
-
27
- // Rate 3
28
- if (price - 250000 > 0) {
29
- if ((price - 250000) > (400000 - 250000)) {
30
- rate3 = (400000 - 250000) * 0.10;
31
- } else {
32
- rate3 = (price - 250000) * 0.10;
33
- }
34
- }
35
-
36
- // Rate 4
37
- if (price - 400000 > 0) {
38
- if ((price - 400000) > (750000 - 400000)) {
39
- rate4 = (750000 - 400000) * 0.125;
40
- } else {
41
- rate4 = (price - 400000) * 0.125;
42
- }
43
- }
44
-
45
- // Rate 5
46
- if (price - 750000 > 0) {
47
- if ((price - 750000) > (1500000 - 750000)) {
48
- rate5 = (1500000 - 750000) * 0.15;
49
- } else {
50
- rate5 = (price - 750000) * 0.15;
51
- }
52
- }
53
-
54
- // Rate 6
55
- if (price - 1500000 > 0) {
56
- rate6 = (price - 1500000) * 0.17;
57
- }
58
- }
59
-
60
- if (!additionalProperty) {
61
- // Rate 2
62
- if (price - 225000 > 0) {
63
- if ((price - 225000) > (400000 - 225000)) {
64
- rate2 = (400000 - 225000) * 0.06;
65
- } else {
66
- rate2 = (price - 225000) * 0.06;
67
- }
68
- }
69
-
70
- // Rate 3
71
- if (price - 400000 > 0) {
72
- if ((price - 400000) > (750000 - 400000)) {
73
- rate3 = (750000 - 400000) * 0.075;
74
- } else {
75
- rate3 = (price - 400000) * 0.075;
76
- }
77
- }
78
-
79
- // Rate 4
80
- if (price - 750000 > 0) {
81
- if ((price - 750000) > (1500000 - 750000)) {
82
- rate4 = (1500000 - 750000) * 0.10;
83
- } else {
84
- rate4 = (price - 750000) * 0.10;
85
- }
86
- }
87
-
88
- // Rate 5
89
- if (price - 1500000 > 0) {
90
- rate5 = (price - 1500000) * 0.12;
91
- }
92
- }
93
-
94
- totalRate = rate1 + rate2 + rate3 + rate4 + rate5 + rate6;
95
- effectiveRate = totalRate / price;
96
- if (!ukResident) {
97
- totalRate = totalRate * (effectiveRate + 0.02) / effectiveRate;
98
- effectiveRate += 0.02;
99
- }
100
- effectiveRate *= 100;
101
-
102
- return {
103
- rate1,
104
- rate2,
105
- rate3,
106
- rate4,
107
- rate5,
108
- rate6,
109
- totalRate,
110
- effectiveRate: Math.round(effectiveRate * 10) / 10,
111
- };
112
- };
113
-
114
- export default welshRates;