UNPKG

@blocklet/payment-react

Version:

Reusable react components for payment kit v2

335 lines (334 loc) 12.5 kB
"use strict"; 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) })] })] }); }