@blocklet/payment-react
Version:
Reusable react components for payment kit v2
335 lines (334 loc) • 12.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
module.exports = ProductItem;
var _jsxRuntime = require("react/jsx-runtime");
var _context = require("@arcblock/ux/lib/Locale/context");
var _material = require("@mui/material");
var _iconsMaterial = require("@mui/icons-material");
var _react = require("react");
var _status = _interopRequireDefault(require("../components/status"));
var _switchButton = _interopRequireDefault(require("../components/switch-button"));
var _util = require("../libs/util");
var _productCard = _interopRequireDefault(require("./product-card"));
var _dayjs = _interopRequireDefault(require("../libs/dayjs"));
var _payment = require("../contexts/payment");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function ProductItem({
item,
items,
trialInDays,
trialEnd = 0,
currency,
mode = "normal",
children = null,
onUpsell,
onDownsell,
completed = false,
adjustableQuantity = {
enabled: false
},
onQuantityChange = () => {}
}) {
const {
t,
locale
} = (0, _context.useLocaleContext)();
const {
settings
} = (0, _payment.usePaymentContext)();
const pricing = (0, _util.formatLineItemPricing)(item, currency, {
trialEnd,
trialInDays
}, locale);
const saving = (0, _util.formatUpsellSaving)(items, currency);
const metered = item.price?.recurring?.usage_type === "metered" ? t("common.metered") : "";
const canUpsell = mode === "normal" && items.length === 1;
const isCreditProduct = item.price.product?.type === "credit" && item.price.metadata?.credit_config?.credit_amount;
const creditAmount = isCreditProduct ? Number(item.price.metadata.credit_config.credit_amount) : 0;
const creditCurrency = isCreditProduct ? (0, _util.findCurrency)(settings.paymentMethods, item.price.metadata?.credit_config?.currency_id ?? "") : null;
const validDuration = item.price.metadata?.credit_config?.valid_duration_value;
const validDurationUnit = item.price.metadata?.credit_config?.valid_duration_unit || "days";
const [localQuantity, setLocalQuantity] = (0, _react.useState)(item.quantity);
const canAdjustQuantity = adjustableQuantity.enabled && mode === "normal";
const minQuantity = Math.max(adjustableQuantity.minimum || 1, 1);
const quantityAvailable = Math.min(item.price.quantity_limit_per_checkout, item.price.quantity_available);
const maxQuantity = quantityAvailable ? Math.min(adjustableQuantity.maximum || Infinity, quantityAvailable) : adjustableQuantity.maximum || Infinity;
const handleQuantityChange = newQuantity => {
if (newQuantity >= minQuantity && newQuantity <= maxQuantity) {
if ((0, _util.formatQuantityInventory)(item.price, newQuantity, locale)) {
return;
}
setLocalQuantity(newQuantity);
onQuantityChange(item.price_id, newQuantity);
}
};
const handleQuantityIncrease = () => {
if (localQuantity < maxQuantity) {
handleQuantityChange(localQuantity + 1);
}
};
const handleQuantityDecrease = () => {
if (localQuantity > minQuantity) {
handleQuantityChange(localQuantity - 1);
}
};
const handleQuantityInputChange = event => {
const value = parseInt(event.target.value, 10);
if (!Number.isNaN(value)) {
handleQuantityChange(value);
}
};
const formatCreditInfo = () => {
if (!isCreditProduct) return null;
const isRecurring = item.price.type === "recurring";
const totalCredit = (0, _util.formatNumber)(creditAmount * localQuantity);
let message = "";
if (isRecurring) {
message = t("payment.checkout.credit.recurringInfo", {
amount: totalCredit,
period: (0, _util.formatRecurring)(item.price.recurring, true, "per", locale)
});
} else {
message = t("payment.checkout.credit.oneTimeInfo", {
amount: totalCredit,
symbol: creditCurrency?.symbol || "Credits"
});
}
if (validDuration && validDuration > 0) {
message += `\uFF0C${t("payment.checkout.credit.expiresIn", {
duration: validDuration,
unit: t(`common.${validDurationUnit}`)
})}`;
}
return message;
};
const primaryText = (0, _react.useMemo)(() => {
const price = item.upsell_price || item.price || {};
const isRecurring = price?.type === "recurring" && price?.recurring;
const trial = trialInDays > 0 || trialEnd > (0, _dayjs.default)().unix();
if (isRecurring && !trial && price?.recurring?.usage_type !== "metered") {
return `${pricing.primary} ${price.recurring ? (0, _util.formatRecurring)(price.recurring, false, "slash", locale) : ""}`;
}
return pricing.primary;
}, [trialInDays, trialEnd, pricing, item, locale]);
const quantityInventoryError = (0, _util.formatQuantityInventory)(item.price, localQuantity, locale);
return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
spacing: 1,
className: "product-item",
sx: {
alignItems: "flex-start",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
className: "product-item-content",
sx: {
alignItems: "flex-start",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 0.5,
sx: {
alignItems: "center",
flexWrap: "wrap",
justifyContent: "space-between",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_productCard.default, {
logo: item.price.product?.images[0],
name: item.price.product?.name,
extra: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
sx: {
display: "flex",
alignItems: "center"
},
children: item.price.type === "recurring" && item.price.recurring ? [pricing.quantity, t("common.billed", {
rule: `${(0, _util.formatRecurring)(item.upsell_price?.recurring || item.price.recurring, true, "per", locale)} ${metered}`
})].filter(Boolean).join(", ") : pricing.quantity
})
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
sx: {
alignItems: "flex-end",
flex: 1
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
color: "text.primary",
fontWeight: 500,
whiteSpace: "nowrap"
},
gutterBottom: true,
children: primaryText
}), pricing.secondary && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
fontSize: "0.74375rem",
color: "text.lighter"
},
children: pricing.secondary
})]
})]
}), quantityInventoryError ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_status.default, {
label: quantityInventoryError,
variant: "outlined",
sx: {
mt: 1,
borderColor: "chip.error.border",
backgroundColor: "chip.error.background",
color: "chip.error.text"
}
}) : null, canAdjustQuantity && !completed && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
sx: {
mt: 1,
p: 1
},
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 1,
sx: {
alignItems: "center"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
variant: "body2",
sx: {
color: "text.secondary",
minWidth: "fit-content"
},
children: [t("common.quantity"), ":"]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
size: "small",
onClick: handleQuantityDecrease,
disabled: localQuantity <= minQuantity,
sx: {
minWidth: 32,
width: 32,
height: 32
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.Remove, {
fontSize: "small"
})
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.TextField, {
size: "small",
value: localQuantity,
onChange: handleQuantityInputChange,
type: "number",
slotProps: {
htmlInput: {
min: minQuantity,
max: maxQuantity,
style: {
textAlign: "center",
padding: "4px",
minWidth: 80
}
}
}
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
size: "small",
onClick: handleQuantityIncrease,
disabled: localQuantity >= maxQuantity,
sx: {
minWidth: 32,
width: 32,
height: 32
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.Add, {
fontSize: "small"
})
})]
})
}), isCreditProduct && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
severity: "info",
sx: {
mt: 1,
fontSize: "0.875rem"
},
icon: false,
children: formatCreditInfo()
}), children]
}), canUpsell && !item.upsell_price_id && item.price.upsell?.upsells_to && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
className: "product-item-upsell",
sx: {
alignItems: "center",
justifyContent: "space-between",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
component: "label",
htmlFor: "upsell-switch",
sx: {
fontSize: 12,
cursor: "pointer",
color: "text.secondary"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_switchButton.default, {
id: "upsell-switch",
sx: {
mr: 1
},
variant: "success",
checked: false,
onChange: () => onUpsell(item.price_id, item.price.upsell?.upsells_to_id)
}), t("payment.checkout.upsell.save", {
recurring: (0, _util.formatRecurring)(item.price.upsell.upsells_to.recurring, true, "per", locale)
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_status.default, {
label: t("payment.checkout.upsell.off", {
saving
}),
variant: "outlined",
sx: {
ml: 1,
borderColor: "chip.warning.border",
backgroundColor: "chip.warning.background",
color: "chip.warning.text"
}
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
component: "span",
sx: {
fontSize: 12
},
children: (0, _util.formatPrice)(item.price.upsell.upsells_to, currency, item.price.product?.unit_label, 1, true, locale)
})]
}), canUpsell && item.upsell_price_id && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
className: "product-item-upsell",
sx: {
alignItems: "center",
justifyContent: "space-between",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
component: "label",
htmlFor: "upsell-switch",
sx: {
fontSize: 12,
cursor: "pointer",
color: "text.secondary"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_switchButton.default, {
id: "upsell-switch",
sx: {
mr: 1
},
variant: "success",
checked: true,
onChange: () => onDownsell(item.upsell_price_id)
}), t("payment.checkout.upsell.revert", {
recurring: t(`common.${(0, _util.formatRecurring)(item.price.recurring)}`)
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
component: "span",
sx: {
fontSize: 12
},
children: (0, _util.formatPrice)(item.price, currency, item.price.product?.unit_label, 1, true, locale)
})]
})]
});
}