| 1 |
import { ExperimentalOrderMeta, registerCheckoutFilters, TotalsItem } from "@woocommerce/blocks-checkout"; |
| 2 |
import { FormattedMonetaryAmount } from "@woocommerce/blocks-components"; |
| 3 |
import { __, sprintf } from "@wordpress/i18n"; |
| 4 |
import { registerPlugin } from "@wordpress/plugins"; |
| 5 |
|
| 6 |
import { formatPrice, getCurrencyFromPriceResponse } from "@woocommerce/price-format"; |
| 7 |
// import { useStoreCart } from "@woocommerce/base-context/hooks"; |
| 8 |
|
| 9 |
const modifyCartItemPrice = (defaultValue, extensions, args, validation) => { |
| 10 |
const { sdevs_subscription } = extensions; |
| 11 |
const { cartItem } = args; |
| 12 |
const { totals } = cartItem; |
| 13 |
|
| 14 |
if (totals === undefined) { |
| 15 |
return defaultValue; |
| 16 |
} |
| 17 |
if (totals.line_total === "0") { |
| 18 |
return `<price/> ${__("Due Today", "subscription")}`; |
| 19 |
} |
| 20 |
if (sdevs_subscription && sdevs_subscription.type) { |
| 21 |
// Capitalize the first letter to match product page display |
| 22 |
const capitalizedType = sdevs_subscription.type.charAt(0).toUpperCase() + sdevs_subscription.type.slice(1); |
| 23 |
|
| 24 |
// Check max_no_payment - handle string, number, null, undefined |
| 25 |
const maxPayments = parseInt(sdevs_subscription.max_no_payment, 10); |
| 26 |
const paymentInfo = !isNaN(maxPayments) && maxPayments > 0 ? ` x ${maxPayments}` : ""; |
| 27 |
return `<price/> / ${ |
| 28 |
sdevs_subscription.time && sdevs_subscription.time > 1 ? " " + sdevs_subscription.time + "-" : "" |
| 29 |
}${capitalizedType}${paymentInfo}`; |
| 30 |
} |
| 31 |
return defaultValue; |
| 32 |
}; |
| 33 |
|
| 34 |
const modifySubtotalPriceFormat = (defaultValue, extensions, args, validation) => { |
| 35 |
const { sdevs_subscription } = extensions; |
| 36 |
const { sdevs_subscription: recurrings } = extensions; |
| 37 |
|
| 38 |
if (sdevs_subscription && sdevs_subscription.type) { |
| 39 |
// Capitalize the first letter to match product page display |
| 40 |
const capitalizedType = sdevs_subscription.type.charAt(0).toUpperCase() + sdevs_subscription.type.slice(1); |
| 41 |
|
| 42 |
// Check max_no_payment - handle string, number, null, undefined |
| 43 |
const maxPayments = parseInt(sdevs_subscription.max_no_payment, 10); |
| 44 |
const paymentInfo = !isNaN(maxPayments) && maxPayments > 0 ? ` x ${maxPayments}` : ""; |
| 45 |
|
| 46 |
const timePart = sdevs_subscription.time && sdevs_subscription.time > 1 ? sdevs_subscription.time + "-" : ""; |
| 47 |
const priceText = "<price/>\u00A0" + __("Every", "subscription") + " " + timePart + capitalizedType + paymentInfo; |
| 48 |
|
| 49 |
return priceText; |
| 50 |
} |
| 51 |
|
| 52 |
return defaultValue; |
| 53 |
}; |
| 54 |
|
| 55 |
registerCheckoutFilters("sdevs-subscription", { |
| 56 |
cartItemPrice: modifyCartItemPrice, |
| 57 |
subtotalPriceFormat: modifySubtotalPriceFormat, |
| 58 |
}); |
| 59 |
|
| 60 |
const RecurringTotals = ({ cart, extensions }) => { |
| 61 |
if (Object.keys(extensions).length === 0) { |
| 62 |
return; |
| 63 |
} |
| 64 |
const { cartTotals } = cart; |
| 65 |
const { sdevs_subscription: recurrings } = extensions; |
| 66 |
if (recurrings.length === 0) { |
| 67 |
return; |
| 68 |
} |
| 69 |
|
| 70 |
const currency = getCurrencyFromPriceResponse(cartTotals); |
| 71 |
const multiplier = Math.pow(10, currency.minorUnit); |
| 72 |
|
| 73 |
return ( |
| 74 |
<TotalsItem |
| 75 |
className="wc-block-components-totals-footer-item" |
| 76 |
label={__("Recurring totals", "subscription")} |
| 77 |
description={ |
| 78 |
<div style={{ display: "grid" }}> |
| 79 |
{recurrings.map((recurring) => { |
| 80 |
// Capitalize the first letter to match product page display |
| 81 |
const capitalizedType = recurring.type.charAt(0).toUpperCase() + recurring.type.slice(1); |
| 82 |
|
| 83 |
const priceFormatArgs = { |
| 84 |
currency: currency.code, |
| 85 |
currency_symbol: currency.symbol, |
| 86 |
decimal_separator: currency.decimalSeparator, |
| 87 |
thousand_separator: currency.thousandSeparator, |
| 88 |
precision: currency.minorUnit, |
| 89 |
price_format: currency.priceFormat, |
| 90 |
}; |
| 91 |
|
| 92 |
/** |
| 93 |
* Format an amount the same way the surrounding totals are formatted. |
| 94 |
* |
| 95 |
* @param {number} amount Amount in major units. |
| 96 |
* @return {string} Formatted price. |
| 97 |
*/ |
| 98 |
const format = (amount) => formatPrice(Math.round(amount * multiplier), priceFormatArgs); |
| 99 |
|
| 100 |
const recurringLimit = parseInt(recurring.recurring_limit, 10) || 0; |
| 101 |
|
| 102 |
// Billing period as plain text, so notes can quote a price the same way the row does. |
| 103 |
const periodLabel = |
| 104 |
recurring.time && recurring.time > 1 ? `${recurring.time}-${capitalizedType}` : capitalizedType; |
| 105 |
|
| 106 |
return ( |
| 107 |
<div style={{ margin: "20px 0", float: "right" }}> |
| 108 |
<div style={{ fontSize: "18px" }}> |
| 109 |
{recurring.has_recurring_discount && ( |
| 110 |
<del aria-hidden="true" style={{ marginRight: "6px", opacity: 0.6 }}> |
| 111 |
{format(recurring.full_price)} |
| 112 |
</del> |
| 113 |
)} |
| 114 |
<FormattedMonetaryAmount currency={currency} value={Math.round(recurring.price * multiplier)} /> |
| 115 |
<span class="wpsubs-subscription-timing"> |
| 116 |
/ |
| 117 |
{recurring.time && recurring.time > 1 |
| 118 |
? `${recurring.time + "-" + capitalizedType} ` |
| 119 |
: capitalizedType} |
| 120 |
</span> |
| 121 |
</div> |
| 122 |
<small>{recurring.description}</small> |
| 123 |
{recurring.has_one_time_discount && ( |
| 124 |
<> |
| 125 |
<br /> |
| 126 |
<small> |
| 127 |
{sprintf( |
| 128 |
// translators: 1: amount paid today, 2: amount charged on each renewal. |
| 129 |
__("You pay %1$s today. %2$s will be charged from the next renewal.", "subscription"), |
| 130 |
format(recurring.first_price), |
| 131 |
format(recurring.price), |
| 132 |
)} |
| 133 |
</small> |
| 134 |
</> |
| 135 |
)} |
| 136 |
{recurring.has_recurring_discount && recurringLimit > 1 && ( |
| 137 |
<> |
| 138 |
<br /> |
| 139 |
<small> |
| 140 |
{sprintf( |
| 141 |
// translators: 1: number of discounted payments, 2: full price charged afterwards. |
| 142 |
__("Discount applies to your first %1$s payments. After that, %2$s.", "subscription"), |
| 143 |
recurringLimit, |
| 144 |
`${format(recurring.full_price)} / ${periodLabel}`, |
| 145 |
)} |
| 146 |
</small> |
| 147 |
</> |
| 148 |
)} |
| 149 |
{recurring.can_user_cancel === "yes" && |
| 150 |
(recurring.max_no_payment === "" || parseInt(recurring.max_no_payment) === 0) && ( |
| 151 |
<> |
| 152 |
<br /> |
| 153 |
<small>{__("You can cancel subscription at any time!", "subscription")} </small> |
| 154 |
</> |
| 155 |
)} |
| 156 |
{parseInt(recurring.max_no_payment) > 0 && ( |
| 157 |
<> |
| 158 |
<br /> |
| 159 |
<small> |
| 160 |
{sprintf( |
| 161 |
// translators: 1: number of installments, 2: total amount. |
| 162 |
__( |
| 163 |
"This subscription will be billed in %1$s installments, for a total of %2$s.", |
| 164 |
"subscription", |
| 165 |
), |
| 166 |
recurring.max_no_payment, |
| 167 |
format(recurring.price * parseInt(recurring.max_no_payment)), |
| 168 |
)} |
| 169 |
</small> |
| 170 |
</> |
| 171 |
)} |
| 172 |
</div> |
| 173 |
); |
| 174 |
})} |
| 175 |
</div> |
| 176 |
} |
| 177 |
></TotalsItem> |
| 178 |
); |
| 179 |
}; |
| 180 |
|
| 181 |
const render = () => { |
| 182 |
return ( |
| 183 |
<ExperimentalOrderMeta> |
| 184 |
<RecurringTotals /> |
| 185 |
</ExperimentalOrderMeta> |
| 186 |
); |
| 187 |
}; |
| 188 |
|
| 189 |
registerPlugin("sdevs-subscription", { |
| 190 |
render, |
| 191 |
scope: "woocommerce-checkout", |
| 192 |
}); |
| 193 |
|