@blocklet/payment-react 1.14.27 → 1.14.29

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.
Files changed (47) hide show
  1. package/es/checkout/form.js +3 -3
  2. package/es/checkout/table.js +11 -3
  3. package/es/components/livemode.js +1 -1
  4. package/es/components/pricing-table.js +55 -14
  5. package/es/components/table.js +1 -1
  6. package/es/history/invoice/list.js +12 -10
  7. package/es/libs/util.d.ts +1 -0
  8. package/es/libs/util.js +6 -0
  9. package/es/locales/en.js +4 -3
  10. package/es/locales/zh.js +6 -5
  11. package/es/payment/form/address.js +1 -1
  12. package/es/payment/index.d.ts +1 -1
  13. package/es/payment/index.js +4 -1
  14. package/es/payment/product-card.js +2 -2
  15. package/es/payment/skeleton/overview.js +3 -6
  16. package/es/payment/skeleton/payment.js +8 -7
  17. package/lib/checkout/form.js +3 -3
  18. package/lib/checkout/table.js +12 -2
  19. package/lib/components/livemode.js +1 -1
  20. package/lib/components/pricing-table.js +90 -41
  21. package/lib/components/table.js +1 -1
  22. package/lib/history/invoice/list.js +10 -8
  23. package/lib/libs/util.d.ts +1 -0
  24. package/lib/libs/util.js +7 -0
  25. package/lib/locales/en.js +4 -3
  26. package/lib/locales/zh.js +6 -5
  27. package/lib/payment/form/address.js +4 -0
  28. package/lib/payment/index.d.ts +1 -1
  29. package/lib/payment/index.js +8 -1
  30. package/lib/payment/product-card.js +2 -2
  31. package/lib/payment/skeleton/overview.js +9 -14
  32. package/lib/payment/skeleton/payment.js +17 -9
  33. package/package.json +3 -3
  34. package/src/checkout/form.tsx +3 -3
  35. package/src/checkout/table.tsx +11 -3
  36. package/src/components/livemode.tsx +1 -1
  37. package/src/components/pricing-table.tsx +103 -48
  38. package/src/components/table.tsx +1 -1
  39. package/src/history/invoice/list.tsx +11 -10
  40. package/src/libs/util.ts +7 -0
  41. package/src/locales/en.tsx +4 -2
  42. package/src/locales/zh.tsx +5 -4
  43. package/src/payment/form/address.tsx +1 -1
  44. package/src/payment/index.tsx +4 -1
  45. package/src/payment/product-card.tsx +2 -2
  46. package/src/payment/skeleton/overview.tsx +1 -2
  47. package/src/payment/skeleton/payment.tsx +10 -11
@@ -23,6 +23,7 @@ import { useSetState } from 'ahooks';
23
23
  import { useEffect, useMemo, useState } from 'react';
24
24
 
25
25
  import { BN } from '@ocap/util';
26
+ import { isEmpty } from 'lodash';
26
27
  import { usePaymentContext } from '../contexts/payment';
27
28
  import {
28
29
  formatError,
@@ -32,6 +33,16 @@ import {
32
33
  getPriceUintAmountByCurrency,
33
34
  } from '../libs/util';
34
35
  import Amount from '../payment/amount';
36
+ import { useMobile } from '../hooks/mobile';
37
+
38
+ type SortOrder = { [key: string]: number };
39
+
40
+ const sortOrder: SortOrder = {
41
+ year: 1,
42
+ month: 2,
43
+ day: 3,
44
+ hour: 4,
45
+ };
35
46
 
36
47
  const groupItemsByRecurring = (items: TPricingTableItem[], currency: { id: string; symbol: string }) => {
37
48
  const grouped: { [key: string]: TPricingTableItem[] } = {};
@@ -39,7 +50,7 @@ const groupItemsByRecurring = (items: TPricingTableItem[], currency: { id: strin
39
50
 
40
51
  items.forEach((x) => {
41
52
  const key = [x.price.recurring?.interval, x.price.recurring?.interval_count].join('-');
42
- if (x.price.currency_options.find((c: PriceCurrency) => c.currency_id === currency.id)) {
53
+ if (x.price.currency_options?.find((c: PriceCurrency) => c.currency_id === currency.id)) {
43
54
  recurring[key] = x.price.recurring as PriceRecurring;
44
55
  }
45
56
  if (!grouped[key]) {
@@ -69,11 +80,30 @@ PricingTable.defaultProps = {
69
80
 
70
81
  export default function PricingTable({ table, alignItems, interval, mode, onSelect }: Props) {
71
82
  const { t, locale } = useLocaleContext();
83
+ const { isMobile } = useMobile();
72
84
  const {
73
85
  settings: { paymentMethods = [] },
74
86
  } = usePaymentContext();
75
87
  const [currency, setCurrency] = useState(table.currency || {});
76
88
  const { recurring, grouped } = useMemo(() => groupItemsByRecurring(table.items, currency), [table.items, currency]);
89
+ const recurringKeysList = useMemo(() => {
90
+ if (isEmpty(recurring)) {
91
+ return [];
92
+ }
93
+ return Object.keys(recurring).sort((a, b) => {
94
+ const [aType, aValue] = a.split('-');
95
+ const [bType, bValue] = b.split('-');
96
+ if (sortOrder[aType] !== sortOrder[bType]) {
97
+ return sortOrder[aType] - sortOrder[bType];
98
+ }
99
+ if (aValue && bValue) {
100
+ // @ts-ignore
101
+ return bValue - aValue;
102
+ }
103
+ // @ts-ignore
104
+ return b - a;
105
+ });
106
+ }, [recurring]);
77
107
  const [state, setState] = useSetState({ interval });
78
108
  const currencyMap = useMemo(() => {
79
109
  if (!paymentMethods || paymentMethods.length === 0) {
@@ -137,12 +167,12 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
137
167
  gap: 20px;
138
168
  }
139
169
  @media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
140
- .price-table-item {
141
- width: 90% !important;
142
- }
143
- .btn-row {
144
- padding: 0 20px;
145
- }
170
+ // .price-table-item {
171
+ // width: 90% !important;
172
+ // }
173
+ // .btn-row {
174
+ // padding: 0 20px;
175
+ // }
146
176
  }
147
177
  @media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) {
148
178
  .price-table-wrap:has(> div:nth-child(1)) {
@@ -167,45 +197,63 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
167
197
  sm: mode === 'select' ? 3 : 5,
168
198
  },
169
199
  }}>
170
- <div className="btn-row">
171
- {Object.keys(recurring).length > 0 && (
172
- <ToggleButtonGroup
173
- size="small"
174
- value={state.interval}
175
- sx={{
176
- padding: '4px',
177
- borderRadius: '36px',
178
- height: '40px',
179
- boxSizing: 'border-box',
180
- backgroundColor: '#f1f3f5',
181
- border: 0,
182
- }}
183
- onChange={(_, value) => {
184
- if (value !== null) {
185
- setState({ interval: value });
186
- }
187
- }}
188
- exclusive>
189
- {Object.keys(recurring).map((x) => (
190
- <ToggleButton
200
+ <Stack className="btn-row" flexDirection="row">
201
+ {recurringKeysList.length > 0 && (
202
+ <Box>
203
+ {isMobile && recurringKeysList.length > 1 ? (
204
+ <Select
205
+ value={state.interval}
206
+ onChange={(e) => setState({ interval: e.target.value })}
191
207
  size="small"
192
- key={x}
193
- value={x}
208
+ sx={{ m: 1 }}>
209
+ {recurringKeysList.map((x) => (
210
+ <MenuItem key={x} value={x}>
211
+ <Typography color={x === state.interval ? 'text.primary' : 'text.secondary'}>
212
+ {formatRecurring(recurring[x] as PriceRecurring, true, '', locale)}
213
+ </Typography>
214
+ </MenuItem>
215
+ ))}
216
+ </Select>
217
+ ) : (
218
+ <ToggleButtonGroup
219
+ size="small"
220
+ value={state.interval}
194
221
  sx={{
195
- textTransform: 'capitalize',
196
- padding: '5px 12px',
197
- fontSize: '13px',
198
- backgroundColor: x === state.interval ? '#fff !important' : '#f1f3f5 !important',
199
- border: '0px',
200
- '&.Mui-selected': {
201
- borderRadius: '9999px !important',
202
- border: '1px solid #e5e7eb',
203
- },
204
- }}>
205
- {formatRecurring(recurring[x] as PriceRecurring, true, '', locale)}
206
- </ToggleButton>
207
- ))}
208
- </ToggleButtonGroup>
222
+ padding: '4px',
223
+ borderRadius: '36px',
224
+ height: '40px',
225
+ boxSizing: 'border-box',
226
+ backgroundColor: '#f1f3f5',
227
+ border: 0,
228
+ }}
229
+ onChange={(_, value) => {
230
+ if (value !== null) {
231
+ setState({ interval: value });
232
+ }
233
+ }}
234
+ exclusive>
235
+ {recurringKeysList.map((x) => (
236
+ <ToggleButton
237
+ size="small"
238
+ key={x}
239
+ value={x}
240
+ sx={{
241
+ textTransform: 'capitalize',
242
+ padding: '5px 12px',
243
+ fontSize: '13px',
244
+ backgroundColor: x === state.interval ? '#fff !important' : '#f1f3f5 !important',
245
+ border: '0px',
246
+ '&.Mui-selected': {
247
+ borderRadius: '9999px !important',
248
+ border: '1px solid #e5e7eb',
249
+ },
250
+ }}>
251
+ {formatRecurring(recurring[x] as PriceRecurring, true, '', locale)}
252
+ </ToggleButton>
253
+ ))}
254
+ </ToggleButtonGroup>
255
+ )}
256
+ </Box>
209
257
  )}
210
258
  {currencyList.length > 0 && (
211
259
  <Select
@@ -222,7 +270,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
222
270
  ))}
223
271
  </Select>
224
272
  )}
225
- </div>
273
+ </Stack>
226
274
  <Stack
227
275
  flexWrap="wrap"
228
276
  direction="row"
@@ -258,7 +306,10 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
258
306
  borderColor: mode === 'select' && x.is_selected ? 'primary.main' : '#ddd',
259
307
  boxShadow: '0 8px 16px rgba(0, 0, 0, 20%)',
260
308
  },
261
- width: '320px',
309
+ width: {
310
+ xs: '100%',
311
+ md: '320px',
312
+ },
262
313
  maxWidth: '360px',
263
314
 
264
315
  padding: '20px',
@@ -377,8 +428,12 @@ function Subscribe({ x, action, onSelect, currencyId }: any) {
377
428
  fullWidth
378
429
  size="medium"
379
430
  variant="contained"
380
- color={x.is_highlight || x.is_selected ? 'primary' : 'info'}
381
- sx={{ fontSize: '16px', padding: '10px 20px', lineHeight: '28px' }}
431
+ color={x.is_highlight || x.is_selected ? 'info' : 'primary'}
432
+ sx={{
433
+ fontSize: '16px',
434
+ padding: '10px 20px',
435
+ lineHeight: '28px',
436
+ }}
382
437
  loading={state.loading === x.price_id && !state.loaded}
383
438
  disabled={x.is_disabled}
384
439
  onClick={() => handleSelect(x.price_id)}
@@ -93,7 +93,7 @@ const Wrapped = styled(Datatable)`
93
93
  }
94
94
 
95
95
  tr.MuiTableRow-root:not(.MuiTableRow-footer):hover {
96
- background: #f5f5f5;
96
+ background: var(--backgrounds-bg-highlight, #eff6ff);
97
97
  }
98
98
  tr.MuiTableRow-root:last-of-type td:first-of-type {
99
99
  border-bottom-left-radius: 8px;
@@ -124,39 +124,40 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
124
124
 
125
125
  const columns = [
126
126
  {
127
- label: t('payment.customer.invoice.invoiceNumber'),
128
- name: 'number',
127
+ label: t('common.amount'),
128
+ name: 'total',
129
+ width: 60,
130
+ align: 'right',
129
131
  options: {
130
132
  customBodyRenderLite: (_: string, index: number) => {
131
133
  const invoice = data?.list[index] as TInvoiceExpanded;
132
134
  const link = getInvoiceLink(invoice, action);
133
135
  return (
134
136
  <a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
135
- {invoice?.number}
137
+ <Typography>
138
+ {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
139
+ {invoice.paymentCurrency.symbol}
140
+ </Typography>
136
141
  </a>
137
142
  );
138
143
  },
139
144
  },
140
145
  },
141
146
  {
142
- label: t('common.amount'),
143
- name: 'total',
147
+ label: t('payment.customer.invoice.invoiceNumber'),
148
+ name: 'number',
144
149
  options: {
145
150
  customBodyRenderLite: (_: string, index: number) => {
146
151
  const invoice = data?.list[index] as TInvoiceExpanded;
147
152
  const link = getInvoiceLink(invoice, action);
148
153
  return (
149
154
  <a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
150
- <Typography>
151
- {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
152
- {invoice.paymentCurrency.symbol}
153
- </Typography>
155
+ {invoice?.number}
154
156
  </a>
155
157
  );
156
158
  },
157
159
  },
158
160
  },
159
-
160
161
  {
161
162
  label: t('common.updatedAt'),
162
163
  name: 'name',
package/src/libs/util.ts CHANGED
@@ -1035,3 +1035,10 @@ export function formatAmountPrecisionLimit(amount: string, locale = 'en', precis
1035
1035
  }
1036
1036
  return '';
1037
1037
  }
1038
+
1039
+ export function getWordBreakStyle(value: any): 'break-word' | 'break-all' {
1040
+ if (typeof value === 'string' && /\s/.test(value)) {
1041
+ return 'break-word';
1042
+ }
1043
+ return 'break-all';
1044
+ }
@@ -90,6 +90,7 @@ export default flat({
90
90
  quantityLimitPerCheckout: 'Exceed purchase limit',
91
91
  quantityNotEnough: 'Exceed inventory',
92
92
  amountPrecisionLimit: 'Amount decimal places must be less than or equal to {precision}',
93
+ saveAsDefaultPriceSuccess: 'Set default price successfully',
93
94
  },
94
95
  payment: {
95
96
  checkout: {
@@ -198,7 +199,7 @@ export default flat({
198
199
  invoices: 'Invoice History',
199
200
  details: 'Details',
200
201
  summary: 'Summary',
201
- specifics: 'Specifics',
202
+ products: 'Products',
202
203
  update: 'Update Information',
203
204
  empty: 'Seems you do not have any subscriptions or payments here',
204
205
  cancel: {
@@ -234,7 +235,8 @@ export default flat({
234
235
  recover: {
235
236
  button: 'Renew',
236
237
  title: 'Renew your subscription',
237
- description: 'Your subscription will no longer be canceled, it will renew on {date}',
238
+ description:
239
+ 'Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue',
238
240
  },
239
241
  changePlan: {
240
242
  button: 'Update',
@@ -90,6 +90,7 @@ export default flat({
90
90
  quantityLimitPerCheckout: '超出购买限制',
91
91
  quantityNotEnough: '库存不足',
92
92
  amountPrecisionLimit: '金额小数位数必须在 {precision} 位以内',
93
+ saveAsDefaultPriceSuccess: '设置默认价格成功',
93
94
  },
94
95
  payment: {
95
96
  checkout: {
@@ -112,9 +113,9 @@ export default flat({
112
113
  subscription: '查看订阅',
113
114
  invoice: '查看账单',
114
115
  },
115
- paymentRequired: '支付数量',
116
+ paymentRequired: '支付金额',
116
117
  staking: {
117
- title: '质押数量',
118
+ title: '质押金额',
118
119
  tooltip: '质押相当于保证金,用于确保未来的账单能够正常扣款,如果你从 DID Wallet 撤销质押,订阅也会被取消。',
119
120
  },
120
121
  stakingConfirm: '在此支付中,质押金额与产品费用分开',
@@ -195,7 +196,7 @@ export default flat({
195
196
  invoices: '账单历史',
196
197
  details: '账户详情',
197
198
  summary: '计费摘要',
198
- specifics: '具体信息',
199
+ products: '产品信息',
199
200
  update: '更新客户信息',
200
201
  empty: '看起来您在这里没有任何订阅或支付',
201
202
  cancel: {
@@ -228,7 +229,7 @@ export default flat({
228
229
  recover: {
229
230
  button: '续订',
230
231
  title: '续订您的订阅',
231
- description: '您的订阅将不再被取消,将在{date}续订',
232
+ description: '您的订阅将不会被取消,并将在{date}自动续订,请确认是否继续',
232
233
  },
233
234
  changePlan: {
234
235
  button: '更新',
@@ -33,7 +33,7 @@ export default function AddressForm({ mode, stripe, sx = {} }: Props) {
33
33
  placeholder={t('payment.checkout.billing.postal_code')}
34
34
  InputProps={{
35
35
  startAdornment: (
36
- <InputAdornment position="start">
36
+ <InputAdornment position="start" style={{ marginRight: '2px', marginLeft: '-8px' }}>
37
37
  <Controller
38
38
  name="billing_address.country"
39
39
  control={control}
@@ -248,7 +248,7 @@ export default function Payment({
248
248
  paymentLink,
249
249
  customer,
250
250
  completed,
251
- // error,
251
+ error,
252
252
  mode,
253
253
  onPaid,
254
254
  onError,
@@ -281,6 +281,9 @@ export default function Payment({
281
281
  }, [checkoutSession, livemode, setLivemode, refresh]);
282
282
 
283
283
  const renderContent = () => {
284
+ if (error) {
285
+ return <PaymentError mode={mode} title="Oops" description={formatError(error)} />;
286
+ }
284
287
  if (!checkoutSession || !delay) {
285
288
  return (
286
289
  <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
@@ -33,7 +33,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
33
33
  className="cko-ellipsis"
34
34
  variant="body1"
35
35
  title={name}
36
- sx={{ fontWeight: 500, mb: 0.5, lineHeight: 1, fontSize: 16 }}
36
+ sx={{ fontWeight: 500, mb: 0.5, lineHeight: 1.2, fontSize: 16 }}
37
37
  color="text.primary">
38
38
  {name}
39
39
  </Typography>
@@ -41,7 +41,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
41
41
  <Typography
42
42
  variant="body1"
43
43
  title={description}
44
- sx={{ fontSize: '0.85rem', mb: 0.5, lineHeight: 1, textAlign: 'left' }}
44
+ sx={{ fontSize: '0.85rem', mb: 0.5, lineHeight: 1.2, textAlign: 'left' }}
45
45
  color="text.lighter">
46
46
  {description}
47
47
  </Typography>
@@ -5,16 +5,15 @@ export default function OverviewSkeleton() {
5
5
  <Fade in>
6
6
  <Stack direction="column">
7
7
  <Stack direction="row" alignItems="center" spacing={2}>
8
- <Skeleton variant="circular" width={32} height={32} />
9
8
  <Skeleton variant="text" sx={{ fontSize: '2rem', width: '40%' }} />
10
9
  </Stack>
10
+ <Skeleton sx={{ mt: 2 }} variant="rounded" height={100} />
11
11
  <Typography mt={2} component="div" variant="h4">
12
12
  <Skeleton />
13
13
  </Typography>
14
14
  <Typography component="div" variant="h2">
15
15
  <Skeleton />
16
16
  </Typography>
17
- <Skeleton sx={{ mt: 2 }} variant="rounded" width={200} height={200} />
18
17
  </Stack>
19
18
  </Fade>
20
19
  );
@@ -3,28 +3,27 @@ import { Box, Fade, Skeleton, Stack, Typography } from '@mui/material';
3
3
  export default function PaymentSkeleton() {
4
4
  return (
5
5
  <Fade in>
6
- <Stack direction="column" spacing={2}>
6
+ <Stack direction="column">
7
7
  <Skeleton variant="text" sx={{ fontSize: '2rem', width: '40%' }} />
8
- <Box>
9
- <Typography component="div" variant="h4">
8
+ <Skeleton sx={{ mt: 2 }} variant="rounded" height={68} />
9
+ <Box mt={1}>
10
+ <Typography component="div" variant="h4" mb={-1}>
10
11
  <Skeleton />
11
12
  </Typography>
12
- <Typography component="div" variant="h1">
13
- <Skeleton />
13
+ <Typography component="div">
14
+ <Skeleton height={60} />
14
15
  </Typography>
15
16
  </Box>
16
17
  <Box>
17
- <Typography component="div" variant="h4">
18
+ <Typography component="div" variant="h4" mb={-1}>
18
19
  <Skeleton />
19
20
  </Typography>
20
- <Typography component="div" variant="h1">
21
- <Skeleton />
21
+ <Typography component="div">
22
+ <Skeleton height={60} />
22
23
  </Typography>
23
24
  </Box>
24
25
  <Box>
25
- <Typography component="div" variant="h4">
26
- <Skeleton />
27
- </Typography>
26
+ <Skeleton height={60} />
28
27
  </Box>
29
28
  </Stack>
30
29
  </Fade>