@shoppexio/storefront-react 0.5.0 → 0.6.1
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/CHANGELOG.md +9 -0
- package/dist/account.js +327 -27
- package/dist/account.js.map +1 -1
- package/dist/index.d.ts +30 -1
- package/eject/account/account-payment-method.ts +6 -1
- package/eject/account/account-subscriptions.ts +20 -4
- package/eject/account/messages/ar.json +17 -1
- package/eject/account/messages/de.json +17 -1
- package/eject/account/messages/en.json +17 -1
- package/eject/account/messages/es.json +17 -1
- package/eject/account/messages/fi.json +17 -1
- package/eject/account/messages/fr.json +17 -1
- package/eject/account/messages/hi.json +17 -1
- package/eject/account/messages/it.json +17 -1
- package/eject/account/messages/lt.json +17 -1
- package/eject/account/messages/nl.json +17 -1
- package/eject/account/messages/pl.json +17 -1
- package/eject/account/messages/pt.json +17 -1
- package/eject/account/messages/ru.json +17 -1
- package/eject/account/messages/tr.json +17 -1
- package/eject/account/messages/uk.json +17 -1
- package/eject/account/messages/zh.json +17 -1
- package/eject/account/subscriptions.tsx +56 -3
- package/package.json +1 -1
|
@@ -263,6 +263,21 @@
|
|
|
263
263
|
"billingOther": "Оплата кожні {count} × {interval}",
|
|
264
264
|
"gateway": "Платіжний провайдер",
|
|
265
265
|
"paymentMethod": "Спосіб оплати",
|
|
266
|
+
"discount": "Знижка",
|
|
267
|
+
"discountPercent": "{percent}%",
|
|
268
|
+
"discountCap": ", не більше {amount} за платіж",
|
|
269
|
+
"discountEvery": "{code} · знижка {off} на кожен платіж{cap}",
|
|
270
|
+
"discountLeft": "{code} · знижка {off}{cap} · залишилося платежів: {count}, потім {price} {period}",
|
|
271
|
+
"discountLeftOne": "{code} · знижка {off}{cap} · залишився 1 платіж, потім {price} {period}",
|
|
272
|
+
"periodDayOne": "на день",
|
|
273
|
+
"periodDayOther": "раз на {count} дн.",
|
|
274
|
+
"periodWeekOne": "на тиждень",
|
|
275
|
+
"periodWeekOther": "раз на {count} тиж.",
|
|
276
|
+
"periodMonthOne": "на місяць",
|
|
277
|
+
"periodMonthOther": "раз на {count} міс.",
|
|
278
|
+
"periodYearOne": "на рік",
|
|
279
|
+
"periodYearOther": "раз на {count} р.",
|
|
280
|
+
"periodOther": "раз на {count} {interval}",
|
|
266
281
|
"cardOnFile": "{brand}, що закінчується на {last4}",
|
|
267
282
|
"noPaymentMethod": "Не збережено",
|
|
268
283
|
"pause": "Призупинити",
|
|
@@ -350,7 +365,8 @@
|
|
|
350
365
|
"historyFirstOrder": "Перше замовлення",
|
|
351
366
|
"historyRenewal": "Продовження",
|
|
352
367
|
"historyPlanChange": "Зміна тарифу: {from} на {to}",
|
|
353
|
-
"historyDays": "за {days} з {total} дн."
|
|
368
|
+
"historyDays": "за {days} з {total} дн.",
|
|
369
|
+
"historyCouponPayment": "{code} · {payment} з {total}"
|
|
354
370
|
},
|
|
355
371
|
"favorites": {
|
|
356
372
|
"loading": "Завантажуємо ваше обране",
|
|
@@ -263,6 +263,21 @@
|
|
|
263
263
|
"billingOther": "每 {count} × {interval} 计费",
|
|
264
264
|
"gateway": "支付服务商",
|
|
265
265
|
"paymentMethod": "支付方式",
|
|
266
|
+
"discount": "折扣",
|
|
267
|
+
"discountPercent": "{percent}%",
|
|
268
|
+
"discountCap": ",每次最多 {amount}",
|
|
269
|
+
"discountEvery": "{code} · 每次付款优惠 {off}{cap}",
|
|
270
|
+
"discountLeft": "{code} · 优惠 {off}{cap} · 还剩 {count} 次付款,之后 {period} {price}",
|
|
271
|
+
"discountLeftOne": "{code} · 优惠 {off}{cap} · 还剩 1 次付款,之后 {period} {price}",
|
|
272
|
+
"periodDayOne": "每天",
|
|
273
|
+
"periodDayOther": "每 {count} 天",
|
|
274
|
+
"periodWeekOne": "每周",
|
|
275
|
+
"periodWeekOther": "每 {count} 周",
|
|
276
|
+
"periodMonthOne": "每月",
|
|
277
|
+
"periodMonthOther": "每 {count} 个月",
|
|
278
|
+
"periodYearOne": "每年",
|
|
279
|
+
"periodYearOther": "每 {count} 年",
|
|
280
|
+
"periodOther": "每 {count} {interval}",
|
|
266
281
|
"cardOnFile": "{brand} 尾号 {last4}",
|
|
267
282
|
"noPaymentMethod": "未保存支付方式",
|
|
268
283
|
"pause": "暂停",
|
|
@@ -350,7 +365,8 @@
|
|
|
350
365
|
"historyFirstOrder": "首次订单",
|
|
351
366
|
"historyRenewal": "续订",
|
|
352
367
|
"historyPlanChange": "套餐变更:{from} 改为 {to}",
|
|
353
|
-
"historyDays": "{total} 天中的 {days} 天"
|
|
368
|
+
"historyDays": "{total} 天中的 {days} 天",
|
|
369
|
+
"historyCouponPayment": "{code} · 第 {payment}/{total} 次"
|
|
354
370
|
},
|
|
355
371
|
"favorites": {
|
|
356
372
|
"loading": "正在加载你的收藏",
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
subscriptionTiming,
|
|
48
48
|
subscriptionTimingKey,
|
|
49
49
|
} from './account-subscriptions';
|
|
50
|
+
import { gatewayLabel } from './account-payment-method';
|
|
50
51
|
import { callCustomerApi, safeExternalHref } from './customer-account';
|
|
51
52
|
import { hostedPageHref } from './hosted-page';
|
|
52
53
|
import type {
|
|
@@ -148,7 +149,7 @@ export function AccountSubscriptions() {
|
|
|
148
149
|
dash in its place would read as "free". */}
|
|
149
150
|
{subscription.plan ? (
|
|
150
151
|
<span className="sxa:shrink-0 sxa:text-small sxa:font-semibold sxa:tabular-nums">
|
|
151
|
-
{
|
|
152
|
+
{nextPaymentAmount(subscription)}
|
|
152
153
|
</span>
|
|
153
154
|
) : null}
|
|
154
155
|
<SubscriptionStatus subscription={subscription} />
|
|
@@ -215,6 +216,7 @@ function SubscriptionDetail({
|
|
|
215
216
|
};
|
|
216
217
|
|
|
217
218
|
const billing = subscription.plan ? billingMessage(subscription.plan) : null;
|
|
219
|
+
const discount = discountLine(subscription);
|
|
218
220
|
|
|
219
221
|
return (
|
|
220
222
|
<div>
|
|
@@ -246,7 +248,7 @@ function SubscriptionDetail({
|
|
|
246
248
|
{subscription.plan ? (
|
|
247
249
|
<>
|
|
248
250
|
<p className="sxa:text-h2 sxa:font-bold sxa:tabular-nums">
|
|
249
|
-
{
|
|
251
|
+
{nextPaymentAmount(subscription)}
|
|
250
252
|
</p>
|
|
251
253
|
{billing ? (
|
|
252
254
|
<p className="sxa:mt-1 sxa:text-small sxa:text-muted">{t(billing.key, billing.params)}</p>
|
|
@@ -257,7 +259,7 @@ function SubscriptionDetail({
|
|
|
257
259
|
<p className="sxa:mt-2 sxa:text-small sxa:text-muted">{timingLabel(subscription)}</p>
|
|
258
260
|
|
|
259
261
|
<dl className="sxa:mt-5 sxa:space-y-2.5 sxa:border-t sxa:pt-4 sxa:text-small">
|
|
260
|
-
<DetailRow label={t('subscriptions.gateway')}>{subscription.gateway}</DetailRow>
|
|
262
|
+
<DetailRow label={t('subscriptions.gateway')}>{gatewayLabel(subscription.gateway)}</DetailRow>
|
|
261
263
|
<DetailRow label={t('subscriptions.paymentMethod')}>
|
|
262
264
|
{/* Brand AND last four, or the producer's own word for the method.
|
|
263
265
|
Half a card ("Visa ending —") is not a payment method. */}
|
|
@@ -268,6 +270,13 @@ function SubscriptionDetail({
|
|
|
268
270
|
})
|
|
269
271
|
: subscription.payment_method?.type ?? t('subscriptions.noPaymentMethod')}
|
|
270
272
|
</DetailRow>
|
|
273
|
+
{discount ? (
|
|
274
|
+
// Wraps instead of truncating: the line ends with the price after the term.
|
|
275
|
+
<div className="sxa:flex sxa:items-start sxa:justify-between sxa:gap-4">
|
|
276
|
+
<dt className="sxa:shrink-0 sxa:text-muted">{t('subscriptions.discount')}</dt>
|
|
277
|
+
<dd className="sxa:min-w-0 sxa:text-right sxa:font-medium">{discount}</dd>
|
|
278
|
+
</div>
|
|
279
|
+
) : null}
|
|
271
280
|
</dl>
|
|
272
281
|
</AccountCard>
|
|
273
282
|
|
|
@@ -672,6 +681,41 @@ function renewalCopy(subscription: SdkCustomerSubscription, state: OpenRenewalSt
|
|
|
672
681
|
return { note, payLabel: t('subscriptions.payAmount', { amount }) };
|
|
673
682
|
}
|
|
674
683
|
|
|
684
|
+
/**
|
|
685
|
+
* The plan's coupon term as the buyer reads it (Design Batch 4, feature 1), the portal's line:
|
|
686
|
+
* "LAUNCH20 · 20 % off · 2 more payments, then $9.99 a month", or "TENOFF · 10 % off every
|
|
687
|
+
* payment", with ", up to $3.00 each" when the coupon's cap bounds each payment. Every amount is
|
|
688
|
+
* the producer's `discount`. Null without a running term.
|
|
689
|
+
*/
|
|
690
|
+
export function discountLine(subscription: SdkCustomerSubscription): string | null {
|
|
691
|
+
const { discount, plan } = subscription;
|
|
692
|
+
// The producer reads a term only for the plan it serves.
|
|
693
|
+
if (!discount || !plan) return null;
|
|
694
|
+
const off = discount.discount_type === 'PERCENTAGE'
|
|
695
|
+
? t('subscriptions.discountPercent', { percent: Number(discount.discount_value) })
|
|
696
|
+
: formatAccountAmount(discount.discount_amount, discount.currency);
|
|
697
|
+
const cap = discount.max_discount === null
|
|
698
|
+
? ''
|
|
699
|
+
: t('subscriptions.discountCap', { amount: formatAccountAmount(discount.max_discount, discount.currency) });
|
|
700
|
+
if (discount.payments_total === null) return t('subscriptions.discountEvery', { code: discount.code, off, cap });
|
|
701
|
+
const left = discount.payments_total - discount.payments_used;
|
|
702
|
+
const period = billingMessage(plan, 'period');
|
|
703
|
+
return t(left === 1 ? 'subscriptions.discountLeftOne' : 'subscriptions.discountLeft', {
|
|
704
|
+
code: discount.code,
|
|
705
|
+
off,
|
|
706
|
+
cap,
|
|
707
|
+
count: left,
|
|
708
|
+
price: formatAccountAmount(discount.full_amount, discount.currency),
|
|
709
|
+
period: t(period.key, period.params),
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** What the next payment bills: the term's `next_amount` while it runs, else the plan price. */
|
|
714
|
+
export function nextPaymentAmount(subscription: SdkCustomerSubscription): string | null {
|
|
715
|
+
if (subscription.discount) return formatAccountAmount(subscription.discount.next_amount, subscription.discount.currency);
|
|
716
|
+
return subscription.plan ? formatAccountAmount(subscription.plan.price, subscription.plan.currency) : null;
|
|
717
|
+
}
|
|
718
|
+
|
|
675
719
|
function DetailRow({ label, children }: { label: string; children: ReactNode }) {
|
|
676
720
|
return (
|
|
677
721
|
<div className="sxa:flex sxa:items-center sxa:justify-between sxa:gap-4">
|
|
@@ -732,6 +776,7 @@ export function BillingHistoryList({ payload, reload }: { payload: unknown; relo
|
|
|
732
776
|
{history.invoices.map((invoice) => {
|
|
733
777
|
const title = billingEntryTitle(invoice);
|
|
734
778
|
const days = billingEntryDays(invoice);
|
|
779
|
+
const coupon = invoice.coupon_payment;
|
|
735
780
|
return (
|
|
736
781
|
<li key={invoice.uniqid}>
|
|
737
782
|
<Link
|
|
@@ -744,6 +789,14 @@ export function BillingHistoryList({ payload, reload }: { payload: unknown; relo
|
|
|
744
789
|
{formatAccountDate(invoice.created_at)}
|
|
745
790
|
{days ? ` · ${t(days.key, days.params)}` : null}
|
|
746
791
|
</p>
|
|
792
|
+
{/* A discounted payment names its code and which term payment it was, as the dashboard does: "LAUNCH20 · 2 of 3", the code alone when every payment is discounted. Its own line, so it does not wrap after the date at 375 px. */}
|
|
793
|
+
{coupon ? (
|
|
794
|
+
<p className="sxa:mt-0.5 sxa:text-micro sxa:tabular-nums sxa:text-muted">
|
|
795
|
+
{coupon.payments_total === null
|
|
796
|
+
? coupon.code
|
|
797
|
+
: t('subscriptions.historyCouponPayment', { code: coupon.code, payment: coupon.payment, total: coupon.payments_total })}
|
|
798
|
+
</p>
|
|
799
|
+
) : null}
|
|
747
800
|
</div>
|
|
748
801
|
<div className="sxa:flex sxa:shrink-0 sxa:flex-col sxa:items-end sxa:gap-1.5 sxa:sm:flex-row sxa:sm:items-center sxa:sm:gap-3">
|
|
749
802
|
<span className="sxa:text-small sxa:font-semibold sxa:tabular-nums">{formatAccountAmount(invoice.total, invoice.currency)}</span>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoppexio/storefront-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "React hooks, models and a customer account for Shoppex code storefronts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://docs.shoppex.io/storefront/code-storefront-development",
|