@blocklet/payment-react 1.13.127 → 1.13.129

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 (54) hide show
  1. package/es/checkout/form.d.ts +1 -1
  2. package/es/checkout/form.js +2 -1
  3. package/es/checkout/table.js +5 -1
  4. package/es/components/pricing-table.js +1 -0
  5. package/es/{portal → histroy}/invoice/list.js +1 -1
  6. package/es/histroy/mini-invoice/list.d.ts +7 -0
  7. package/es/histroy/mini-invoice/list.js +125 -0
  8. package/es/{portal → histroy}/payment/list.js +0 -1
  9. package/es/index.d.ts +4 -3
  10. package/es/index.js +5 -3
  11. package/es/locales/en.js +2 -0
  12. package/es/locales/zh.js +2 -0
  13. package/es/payment/form/index.js +1 -1
  14. package/es/payment/index.d.ts +2 -2
  15. package/es/payment/index.js +65 -45
  16. package/es/types/index.d.ts +1 -0
  17. package/es/util.d.ts +2 -1
  18. package/es/util.js +4 -0
  19. package/lib/checkout/form.d.ts +1 -1
  20. package/lib/checkout/form.js +2 -0
  21. package/lib/checkout/table.js +12 -5
  22. package/lib/components/pricing-table.js +1 -0
  23. package/lib/{portal → histroy}/invoice/list.js +1 -1
  24. package/lib/histroy/mini-invoice/list.d.ts +7 -0
  25. package/lib/histroy/mini-invoice/list.js +203 -0
  26. package/lib/{portal → histroy}/payment/list.js +0 -9
  27. package/lib/index.d.ts +4 -3
  28. package/lib/index.js +13 -5
  29. package/lib/locales/en.js +2 -0
  30. package/lib/locales/zh.js +2 -0
  31. package/lib/payment/form/index.js +3 -0
  32. package/lib/payment/index.d.ts +2 -2
  33. package/lib/payment/index.js +28 -8
  34. package/lib/types/index.d.ts +1 -0
  35. package/lib/util.d.ts +2 -1
  36. package/lib/util.js +5 -0
  37. package/package.json +3 -3
  38. package/src/checkout/form.tsx +2 -1
  39. package/src/checkout/table.tsx +10 -1
  40. package/src/components/pricing-table.tsx +2 -1
  41. package/src/{portal → histroy}/invoice/list.tsx +1 -1
  42. package/src/histroy/mini-invoice/list.tsx +165 -0
  43. package/src/{portal → histroy}/payment/list.tsx +0 -3
  44. package/src/index.ts +4 -2
  45. package/src/locales/en.tsx +2 -0
  46. package/src/locales/zh.tsx +2 -0
  47. package/src/payment/form/index.tsx +1 -1
  48. package/src/payment/index.tsx +23 -4
  49. package/src/types/index.ts +1 -0
  50. package/src/util.ts +8 -0
  51. /package/es/{portal → histroy}/invoice/list.d.ts +0 -0
  52. /package/es/{portal → histroy}/payment/list.d.ts +0 -0
  53. /package/lib/{portal → histroy}/invoice/list.d.ts +0 -0
  54. /package/lib/{portal → histroy}/payment/list.d.ts +0 -0
@@ -0,0 +1,165 @@
1
+ /* eslint-disable react/no-unstable-nested-components */
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import type { Paginated, TInvoiceExpanded, TSubscriptionExpanded } from '@blocklet/payment-types';
4
+ import { Box, Button, CircularProgress, Divider, List, ListItem, ListSubheader, Typography } from '@mui/material';
5
+ import { fromUnitToToken } from '@ocap/util';
6
+ import { useInfiniteScroll } from 'ahooks';
7
+
8
+ import api from '../../api';
9
+ import Status from '../../components/status';
10
+ import {
11
+ formatSubscriptionProduct,
12
+ formatTime,
13
+ formatToDate,
14
+ getInvoiceStatusColor,
15
+ getSubscriptionStatusColor,
16
+ } from '../../util';
17
+
18
+ const fetchData = (params: Record<string, any> = {}): Promise<Paginated<TInvoiceExpanded>> => {
19
+ const search = new URLSearchParams();
20
+ Object.keys(params).forEach((key) => {
21
+ search.set(key, String(params[key]));
22
+ });
23
+ return api.get(`/api/invoices?${search.toString()}`).then((res: any) => res.data);
24
+ };
25
+
26
+ type Props = {
27
+ subscription: TSubscriptionExpanded;
28
+ };
29
+
30
+ const pageSize = 10;
31
+
32
+ export default function MiniInvoiceList({ subscription }: Props) {
33
+ const { t } = useLocaleContext();
34
+
35
+ const { data, loading } = useInfiniteScroll<Paginated<TInvoiceExpanded>>(
36
+ (d) => {
37
+ const page = d ? Math.ceil(d.list.length / pageSize) + 1 : 1;
38
+ return fetchData({ page, pageSize, status: 'open,paid,uncollectible', subscription_id: subscription.id });
39
+ },
40
+ {
41
+ reloadDeps: [subscription.id],
42
+ }
43
+ );
44
+
45
+ if (loading || !data) {
46
+ return (
47
+ <Position>
48
+ <CircularProgress />
49
+ </Position>
50
+ );
51
+ }
52
+
53
+ if (data && data.list.length === 0) {
54
+ return (
55
+ <Position>
56
+ <Typography color="text.secondary">{t('payment.customer.invoice.empty')}</Typography>
57
+ </Position>
58
+ );
59
+ }
60
+
61
+ const infoList = [
62
+ {
63
+ name: t('payment.customer.subscriptions.plan'),
64
+ value: (
65
+ <Typography fontWeight={600} sx={{ marginRight: '10px' }}>
66
+ {formatSubscriptionProduct(subscription.items)}
67
+ </Typography>
68
+ ),
69
+ },
70
+ {
71
+ name: t('payment.common.status'),
72
+ value: <Status label={subscription.status} color={getSubscriptionStatusColor(subscription.status)} />,
73
+ },
74
+
75
+ {
76
+ name: t('payment.customer.subscriptions.nextInvoice'),
77
+ value: (
78
+ <Typography
79
+ sx={{
80
+ color: '#34BE74',
81
+ fontWeight: 'bold',
82
+ }}>
83
+ {formatTime(subscription.current_period_end * 1000)}
84
+ </Typography>
85
+ ),
86
+ },
87
+ ];
88
+
89
+ return (
90
+ <Position>
91
+ <Typography title={t('payment.checkout.subscription')} />
92
+ <Box
93
+ sx={{
94
+ display: 'flex',
95
+ flexDirection: 'column',
96
+ alignItem: 'center',
97
+ justifyContent: 'flex-start',
98
+ padding: '16px',
99
+ width: '100%',
100
+ height: '100%',
101
+ }}>
102
+ <Typography component="h3" sx={{ textAlign: 'center' }} variant="h5" gutterBottom>
103
+ {t('payment.customer.subscriptions.current')}
104
+ </Typography>
105
+ <Box sx={{ marginTop: '12px' }}>
106
+ <List>
107
+ {infoList.map(({ name, value }) => {
108
+ return (
109
+ <ListItem key={name} disableGutters sx={{ display: 'flex', justifyContent: 'space-between' }}>
110
+ <Typography component="span">{name}</Typography>
111
+ <Typography component="span">{value}</Typography>
112
+ </ListItem>
113
+ );
114
+ })}
115
+ </List>
116
+ </Box>
117
+ <Divider />
118
+ <Box sx={{ marginTop: '12px' }}>
119
+ <List sx={{ overflow: 'auto', maxHeight: { xs: '240px', md: '360px', padding: 0 } }}>
120
+ <ListSubheader disableGutters sx={{ padding: 0 }}>
121
+ <Typography component="h2" variant="h6" fontSize="16px">
122
+ {t('payment.customer.invoices')}
123
+ </Typography>
124
+ </ListSubheader>
125
+ {(data.list || []).map((item: any) => {
126
+ return (
127
+ <ListItem key={item.id} disableGutters sx={{ display: 'flex', justifyContent: 'space-between' }}>
128
+ <Typography component="span">{formatToDate(item.created_at)}</Typography>
129
+ <Typography component="span">
130
+ {fromUnitToToken(item.total, item.paymentCurrency.decimal)}&nbsp;
131
+ {item.paymentCurrency.symbol}
132
+ </Typography>
133
+ <Status label={item.status} color={getInvoiceStatusColor(item.status)} />
134
+ </ListItem>
135
+ );
136
+ })}
137
+ </List>
138
+ </Box>
139
+ <Button target="_top" variant="contained" sx={{ marginTop: 'auto', width: '100%' }} href="">
140
+ {t('payment.customer.subscriptions.title')}
141
+ </Button>
142
+ </Box>
143
+ </Position>
144
+ );
145
+ }
146
+
147
+ function Position({ children }: any) {
148
+ return (
149
+ <Box
150
+ className="mini-invoice-box" // 预留 class 用于设置定位
151
+ sx={{
152
+ position: 'absolute',
153
+ right: 0,
154
+ top: '30px',
155
+ justifyContent: 'center',
156
+ padding: '8px',
157
+ minWidth: '400px',
158
+ background: '#fff',
159
+ zIndex: 9,
160
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 20%)',
161
+ }}>
162
+ {children}
163
+ </Box>
164
+ );
165
+ }
@@ -79,9 +79,6 @@ export default function CustomerPaymentList({ customer_id }: Props) {
79
79
  md: 3,
80
80
  }}
81
81
  flexWrap="nowrap">
82
- <Box flex={3} sx={{ minWidth: '220px' }}>
83
- <Typography component="span">{item.id}</Typography>
84
- </Box>
85
82
  <Box flex={3}>
86
83
  <Typography>{formatToDate(item.created_at)}</Typography>
87
84
  </Box>
package/src/index.ts CHANGED
@@ -8,12 +8,13 @@ import PricingTable from './components/pricing-table';
8
8
  import Status from './components/status';
9
9
  import Switch from './components/switch-button';
10
10
  import dayjs from './dayjs';
11
+ import CustomerInvoiceList from './histroy/invoice/list';
12
+ import MiniInvoiceList from './histroy/mini-invoice/list';
13
+ import CustomerPaymentList from './histroy/payment/list';
11
14
  import Amount from './payment/amount';
12
15
  import PhoneInput from './payment/form/phone';
13
16
  import Payment from './payment/index';
14
17
  import ProductSkeleton from './payment/product-skeleton';
15
- import CustomerInvoiceList from './portal/invoice/list';
16
- import CustomerPaymentList from './portal/payment/list';
17
18
 
18
19
  export * from './util';
19
20
  export * from './contexts/payment';
@@ -37,4 +38,5 @@ export {
37
38
  Amount,
38
39
  CustomerInvoiceList,
39
40
  CustomerPaymentList,
41
+ MiniInvoiceList,
40
42
  };
@@ -213,6 +213,8 @@ export default flat({
213
213
  empty: 'Seems you do not have any payment here',
214
214
  },
215
215
  subscriptions: {
216
+ plan: 'Plan',
217
+ nextInvoice: 'Next Invoice',
216
218
  title: 'Manage subscriptions',
217
219
  current: 'Current subscriptions',
218
220
  empty: 'Seems you do not have any subscriptions here',
@@ -209,6 +209,8 @@ export default flat({
209
209
  empty: '你没有任何支付',
210
210
  },
211
211
  subscriptions: {
212
+ plan: '订阅',
213
+ nextInvoice: '下一张发票',
212
214
  title: '订阅管理',
213
215
  current: '当前订阅',
214
216
  empty: '你还没有任何订阅',
@@ -367,7 +367,7 @@ export default function PaymentForm({
367
367
  <Stack direction="row" alignItems="center">
368
368
  <Avatar src={x.logo} alt={x.name} sx={{ width: 30, height: 30, marginRight: '10px' }} />
369
369
  <div>
370
- <Typography variant="h5" component="div">
370
+ <Typography variant="h5" component="div" sx={{ fontSize: '18px' }}>
371
371
  {x.symbol}
372
372
  </Typography>
373
373
  <Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
@@ -2,10 +2,11 @@
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
4
  import type { TCustomer, TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
5
+ import { ArrowBackOutlined } from '@mui/icons-material';
5
6
  import { Box, Fade, Stack } from '@mui/material';
6
7
  import { styled } from '@mui/system';
7
8
  import { useSetState } from 'ahooks';
8
- import { useEffect } from 'react';
9
+ import { useEffect, useState } from 'react';
9
10
  import { FormProvider, useForm } from 'react-hook-form';
10
11
  import { LiteralUnion } from 'type-fest';
11
12
 
@@ -40,9 +41,18 @@ export default function Payment({
40
41
  mode,
41
42
  onPaid,
42
43
  onError,
44
+ goBack,
43
45
  }: Props) {
44
46
  const { t } = useLocaleContext();
45
47
  const { refresh, livemode, setLivemode } = usePaymentContext();
48
+ const [delay, setDelay] = useState(false);
49
+
50
+ useEffect(() => {
51
+ setTimeout(() => {
52
+ // 骨架屏 delay
53
+ setDelay(true);
54
+ }, 500);
55
+ }, []);
46
56
 
47
57
  useEffect(() => {
48
58
  if (checkoutSession) {
@@ -59,7 +69,7 @@ export default function Payment({
59
69
  return <PaymentError title="Oops" description={formatError(error)} />;
60
70
  }
61
71
 
62
- if (!checkoutSession) {
72
+ if (!checkoutSession || !delay) {
63
73
  return (
64
74
  <Root mode={mode}>
65
75
  <Stack className="cko-container">
@@ -105,6 +115,7 @@ export default function Payment({
105
115
  customer={customer as TCustomer}
106
116
  onPaid={onPaid}
107
117
  onError={onError}
118
+ goBack={goBack}
108
119
  mode={mode}
109
120
  />
110
121
  );
@@ -126,6 +137,7 @@ export function PaymentInner({
126
137
  mode,
127
138
  onPaid,
128
139
  onError,
140
+ goBack,
129
141
  }: MainProps) {
130
142
  const { t } = useLocaleContext();
131
143
  const { settings, session } = usePaymentContext();
@@ -204,6 +216,13 @@ export function PaymentInner({
204
216
  return (
205
217
  <FormProvider {...methods}>
206
218
  <Root mode={mode}>
219
+ {mode !== 'standalone' ? (
220
+ <ArrowBackOutlined
221
+ sx={{ mr: 0.5, color: 'text.secondary', alignSelf: 'flex-start', margin: '16px 0', cursor: 'pointer' }}
222
+ onClick={goBack}
223
+ fontSize="medium"
224
+ />
225
+ ) : null}
207
226
  <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : 8 } }}>
208
227
  <Fade in>
209
228
  <Stack className="cko-overview" direction="column">
@@ -357,11 +376,11 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
357
376
  }
358
377
 
359
378
  .cko-payment-card:nth-child(odd) {
360
- margin-right: 16px;
379
+ margin-right: 8px;
361
380
  }
362
381
 
363
382
  .cko-payment-card-unselect:nth-child(odd) {
364
- margin-right: 16px;
383
+ margin-right: 8px;
365
384
  }
366
385
 
367
386
  .cko-payment-card::after {
@@ -26,4 +26,5 @@ export type CheckoutProps = Partial<CheckoutCallbacks> & {
26
26
  export type CheckoutCallbacks = {
27
27
  onPaid: (res: CheckoutContext) => void;
28
28
  onError: (err: Error) => void;
29
+ goBack?: () => void;
29
30
  };
package/src/util.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  TPaymentMethodExpanded,
10
10
  TPrice,
11
11
  TSubscriptionExpanded,
12
+ TSubscriptionItemExpanded,
12
13
  } from '@blocklet/payment-types';
13
14
  import { BN, fromUnitToToken } from '@ocap/util';
14
15
  import { defaultCountries } from 'react-international-phone';
@@ -551,6 +552,13 @@ export function sleep(ms: number) {
551
552
  });
552
553
  }
553
554
 
555
+ export function formatSubscriptionProduct(items: TSubscriptionItemExpanded[], maxLength = 2) {
556
+ const names = items.map((x) => x.price.product?.name).filter(Boolean);
557
+ return (
558
+ names.slice(0, maxLength).join(', ') + (names.length > maxLength ? ` and ${names.length - maxLength} more` : '')
559
+ );
560
+ }
561
+
554
562
  export const getSubscriptionTimeSummary = (subscription: TSubscriptionExpanded) => {
555
563
  const lines = [`Started on ${formatToDate(subscription.start_date * 1000)}`];
556
564
  if (subscription.status === 'active' || subscription.status === 'trialing') {
File without changes
File without changes
File without changes
File without changes