payment-kit 1.14.21 → 1.14.22

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 (79) hide show
  1. package/blocklet.yml +1 -1
  2. package/package.json +19 -19
  3. package/src/app.tsx +13 -12
  4. package/src/components/balance-list.tsx +12 -2
  5. package/src/components/copyable.tsx +3 -2
  6. package/src/components/customer/edit.tsx +25 -21
  7. package/src/components/customer/form.tsx +18 -28
  8. package/src/components/customer/link.tsx +1 -2
  9. package/src/components/date-range-picker.tsx +21 -0
  10. package/src/components/drawer-form.tsx +27 -4
  11. package/src/components/event/list.tsx +3 -2
  12. package/src/components/filter-toolbar.tsx +11 -4
  13. package/src/components/info-card.tsx +4 -2
  14. package/src/components/info-metric.tsx +2 -2
  15. package/src/components/info-row.tsx +33 -26
  16. package/src/components/invoice/list.tsx +2 -2
  17. package/src/components/invoice/table.tsx +148 -85
  18. package/src/components/invoice-pdf/pdf.tsx +5 -1
  19. package/src/components/layout/admin.tsx +8 -2
  20. package/src/components/metadata/editor.tsx +25 -18
  21. package/src/components/metadata/form.tsx +83 -25
  22. package/src/components/metadata/list.tsx +22 -6
  23. package/src/components/payment-intent/list.tsx +3 -3
  24. package/src/components/payment-link/preview.tsx +42 -24
  25. package/src/components/payouts/list.tsx +2 -3
  26. package/src/components/price/form.tsx +27 -12
  27. package/src/components/price/upsell.tsx +1 -4
  28. package/src/components/pricing-table/preview.tsx +42 -23
  29. package/src/components/product/cross-sell-select.tsx +1 -1
  30. package/src/components/product/cross-sell.tsx +3 -4
  31. package/src/components/product/edit-price.tsx +0 -1
  32. package/src/components/refund/list.tsx +9 -4
  33. package/src/components/section/header.tsx +11 -4
  34. package/src/components/subscription/description.tsx +10 -6
  35. package/src/components/subscription/items/index.tsx +28 -6
  36. package/src/components/subscription/list.tsx +2 -2
  37. package/src/components/subscription/metrics.tsx +10 -8
  38. package/src/components/subscription/portal/actions.tsx +37 -11
  39. package/src/components/subscription/portal/list.tsx +131 -70
  40. package/src/components/subscription/status.tsx +9 -3
  41. package/src/global.css +1 -1
  42. package/src/hooks/mobile.ts +3 -3
  43. package/src/libs/util.ts +6 -2
  44. package/src/locales/en.tsx +37 -1
  45. package/src/locales/zh.tsx +37 -1
  46. package/src/pages/admin/billing/index.tsx +24 -4
  47. package/src/pages/admin/billing/invoices/detail.tsx +302 -147
  48. package/src/pages/admin/billing/subscriptions/detail.tsx +259 -134
  49. package/src/pages/admin/customers/customers/detail.tsx +358 -175
  50. package/src/pages/admin/customers/customers/index.tsx +8 -5
  51. package/src/pages/admin/customers/index.tsx +22 -5
  52. package/src/pages/admin/developers/webhooks/index.tsx +2 -2
  53. package/src/pages/admin/index.tsx +24 -10
  54. package/src/pages/admin/overview.tsx +1 -1
  55. package/src/pages/admin/payments/index.tsx +22 -7
  56. package/src/pages/admin/payments/intents/detail.tsx +263 -121
  57. package/src/pages/admin/payments/payouts/detail.tsx +235 -102
  58. package/src/pages/admin/payments/refunds/detail.tsx +286 -133
  59. package/src/pages/admin/products/index.tsx +28 -12
  60. package/src/pages/admin/products/links/create.tsx +16 -6
  61. package/src/pages/admin/products/links/detail.tsx +280 -176
  62. package/src/pages/admin/products/links/index.tsx +4 -7
  63. package/src/pages/admin/products/passports/index.tsx +6 -3
  64. package/src/pages/admin/products/prices/detail.tsx +260 -139
  65. package/src/pages/admin/products/prices/list.tsx +7 -3
  66. package/src/pages/admin/products/pricing-tables/create.tsx +17 -5
  67. package/src/pages/admin/products/pricing-tables/detail.tsx +221 -121
  68. package/src/pages/admin/products/pricing-tables/index.tsx +1 -2
  69. package/src/pages/admin/products/products/detail.tsx +262 -119
  70. package/src/pages/admin/products/products/index.tsx +1 -2
  71. package/src/pages/admin/settings/index.tsx +27 -7
  72. package/src/pages/customer/index.tsx +431 -143
  73. package/src/pages/customer/invoice/detail.tsx +138 -26
  74. package/src/pages/customer/refund/list.tsx +193 -4
  75. package/src/pages/customer/subscription/change-payment.tsx +20 -20
  76. package/src/pages/customer/subscription/detail.tsx +168 -34
  77. package/src/pages/customer/subscription/embed.tsx +22 -19
  78. package/src/pages/home.tsx +7 -1
  79. package/src/components/table.tsx +0 -93
@@ -2,15 +2,25 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Toast from '@arcblock/ux/lib/Toast';
3
3
  import { api, formatError, formatTime, usePaymentContext } from '@blocklet/payment-react';
4
4
  import type { TPrice, TProduct, TProductExpanded } from '@blocklet/payment-types';
5
- import { ArrowBackOutlined, Edit } from '@mui/icons-material';
6
- import { Alert, AlertTitle, Box, Button, CircularProgress, Grid, Stack, Typography } from '@mui/material';
5
+ import { ArrowBackOutlined } from '@mui/icons-material';
6
+ import {
7
+ Alert,
8
+ AlertTitle,
9
+ Avatar,
10
+ Box,
11
+ Button,
12
+ CircularProgress,
13
+ Divider,
14
+ Grid,
15
+ Stack,
16
+ Typography,
17
+ } from '@mui/material';
7
18
  import { styled } from '@mui/system';
8
19
  import { useRequest, useSetState } from 'ahooks';
9
20
  import { useNavigate } from 'react-router-dom';
10
21
 
11
22
  import Copyable from '../../../../components/copyable';
12
23
  import EventList from '../../../../components/event/list';
13
- import InfoCard from '../../../../components/info-card';
14
24
  import InfoMetric from '../../../../components/info-metric';
15
25
  import InfoRow from '../../../../components/info-row';
16
26
  import MetadataEditor from '../../../../components/metadata/editor';
@@ -27,6 +37,9 @@ const getProduct = (id: string): Promise<TProductExpanded> => {
27
37
  return api.get(`/api/products/${id}`).then((res) => res.data);
28
38
  };
29
39
 
40
+ const InfoDirection = 'column';
41
+ const InfoAlignItems = 'flex-start';
42
+
30
43
  export default function ProductDetail(props: { id: string }) {
31
44
  const { t, locale } = useLocaleContext();
32
45
  const navigate = useNavigate();
@@ -95,6 +108,9 @@ export default function ProductDetail(props: { id: string }) {
95
108
  };
96
109
 
97
110
  const isLocked = data.locked || data.prices.some((x) => x.locked);
111
+ const handleEditMetadata = () => {
112
+ setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
113
+ };
98
114
 
99
115
  return (
100
116
  <Root direction="column" spacing={4} sx={{ mb: 4 }}>
@@ -121,135 +137,262 @@ export default function ProductDetail(props: { id: string }) {
121
137
  {t('admin.products')}
122
138
  </Typography>
123
139
  </Stack>
124
- <Copyable text={props.id} style={{ marginLeft: 4 }} />
140
+ <ProductActions data={data} onChange={onChange} variant="normal" />
125
141
  </Stack>
126
- <Box mt={2}>
142
+ <Box
143
+ mt={4}
144
+ mb={3}
145
+ sx={{
146
+ display: 'flex',
147
+ gap: {
148
+ xs: 2,
149
+ sm: 2,
150
+ md: 5,
151
+ },
152
+ flexWrap: 'wrap',
153
+ flexDirection: {
154
+ xs: 'column',
155
+ sm: 'column',
156
+ md: 'row',
157
+ },
158
+ alignItems: {
159
+ xs: 'flex-start',
160
+ sm: 'flex-start',
161
+ md: 'center',
162
+ },
163
+ }}>
127
164
  <Stack direction="row" justifyContent="space-between" alignItems="center">
128
- <InfoCard
129
- logo={data.images[0]}
130
- name={data.name}
131
- // @ts-ignore
132
- description={formatProductPrice(data, settings.baseCurrency, locale)}
133
- />
134
- <ProductActions data={data} onChange={onChange} variant="normal" />
165
+ <Stack direction="row" alignItems="center" spacing={1}>
166
+ <Avatar src={data.images[0]} alt={data.name} variant="square" sx={{ width: '52px', height: '52px' }} />
167
+ <Stack direction="column" alignItems="flex-start" justifyContent="space-around">
168
+ <Typography variant="h2" color="text.primary">
169
+ {data.name}
170
+ </Typography>
171
+ <Typography variant="subtitle1" color="text.lighter">
172
+ {formatProductPrice(data as any, settings.baseCurrency, locale)}
173
+ </Typography>
174
+ </Stack>
175
+ </Stack>
135
176
  </Stack>
136
177
  <Stack
137
178
  className="section-body"
138
- direction="row"
139
- spacing={3}
140
179
  justifyContent="flex-start"
141
180
  flexWrap="wrap"
142
- sx={{ pt: 2, mt: 2, borderTop: '1px solid #eee' }}>
143
- <InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
144
- <InfoMetric label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
181
+ sx={{
182
+ 'hr.MuiDivider-root:last-child': {
183
+ display: 'none',
184
+ },
185
+ flexDirection: {
186
+ xs: 'column',
187
+ sm: 'column',
188
+ md: 'row',
189
+ },
190
+ alignItems: {
191
+ xs: 'flex-start',
192
+ sm: 'flex-start',
193
+ md: 'center',
194
+ },
195
+ gap: {
196
+ xs: 1,
197
+ sm: 1,
198
+ md: 3,
199
+ },
200
+ }}>
201
+ <InfoMetric label={t('common.id')} value={<Copyable text={props.id} style={{ marginLeft: 4 }} />} divider />
145
202
  </Stack>
146
203
  </Box>
204
+ <Divider />
147
205
  </Box>
148
- <Box className="section">
149
- <SectionHeader title={t('admin.details')}>
150
- <Button
151
- variant="outlined"
152
- color="inherit"
153
- size="small"
154
- onClick={() => setState((prev) => ({ editing: { ...prev.editing, product: true } }))}>
155
- <Edit fontSize="small" sx={{ mr: 0.5 }} />
156
- {t('common.edit')}
157
- </Button>
158
- </SectionHeader>
159
- <Grid className="section-body" container>
160
- <Grid item xs={12} md={6}>
161
- <InfoRow label={t('admin.product.name.label')} value={data.name} />
162
- <InfoRow label={t('admin.product.description.label')} value={data.description} />
163
- <InfoRow label={t('admin.product.statement_descriptor.label')} value={data.statement_descriptor} />
164
- <InfoRow label={t('admin.product.unit_label.label')} value={data.unit_label} />
165
- <InfoRow label={t('admin.product.features.label')} value={data.features.map((x) => x.name).join(',')} />
166
- <InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
167
- <InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
168
- </Grid>
169
- <Grid item xs={12} md={6}>
170
- <InfoRow
171
- label={t('admin.product.image.label')}
172
- value={
173
- data.images.length ? (
174
- <img src={data.images[0]} width={160} height={160} alt={data.name} style={{ objectFit: 'cover' }} />
175
- ) : (
176
- t('empty.image')
177
- )
178
- }
179
- />
180
- </Grid>
181
- {state.editing.product && (
182
- <EditProduct
183
- product={data}
184
- loading={state.loading.product}
185
- onSave={onUpdateAll}
186
- onCancel={() => setState((prev) => ({ editing: { ...prev.editing, product: false } }))}
187
- />
188
- )}
189
- </Grid>
190
- </Box>
191
- <Box className="section">
192
- <SectionHeader title={t('admin.product.pricing')}>
193
- <Button
194
- variant="outlined"
195
- color="inherit"
196
- size="small"
197
- onClick={() => setState((prev) => ({ adding: { ...prev.adding, price: true } }))}>
198
- <Edit fontSize="small" sx={{ mr: 0.5 }} />
199
- {t('admin.price.add')}
200
- </Button>
201
- </SectionHeader>
202
- <Box className="section-body">
203
- <PricesList product={data as any} onChange={runAsync} />
204
- {state.adding.price && (
205
- <AddPrice
206
- loading={state.loading.price}
207
- onSave={onAddPrice}
208
- onCancel={() => setState((prev) => ({ adding: { ...prev.adding, price: false } }))}
209
- />
210
- )}
211
- </Box>
212
- </Box>
213
- <Box className="section">
214
- <SectionHeader title={t('admin.product.cross_sell.title')} />
215
- <Box className="section-body">
216
- <ProductCrossSell data={data} onChange={runAsync} />
217
- </Box>
218
- </Box>
219
- <Box className="section">
220
- <SectionHeader title={t('common.metadata.label')}>
221
- <Button
222
- variant="outlined"
223
- color="inherit"
224
- size="small"
225
- disabled={state.editing.metadata}
226
- onClick={() => setState((prev) => ({ editing: { ...prev.editing, metadata: true } }))}>
227
- <Edit fontSize="small" sx={{ mr: 0.5 }} />
228
- {t('common.metadata.edit')}
229
- </Button>
230
- </SectionHeader>
231
- <Box className="section-body">
232
- <Grid container>
233
- <Grid item xs={12} md={6}>
234
- {!state.editing.metadata && <MetadataList data={data.metadata} />}
206
+
207
+ <Stack
208
+ sx={{
209
+ flexDirection: {
210
+ xs: 'column',
211
+ lg: 'row',
212
+ },
213
+ gap: 4,
214
+ '.payment-link-column-1': {
215
+ minWidth: {
216
+ xs: '100%',
217
+ lg: '600px',
218
+ },
219
+ },
220
+ '.payment-link-column-2': {
221
+ width: {
222
+ xs: '100%',
223
+ md: '100%',
224
+ lg: '580px',
225
+ },
226
+ maxWidth: {
227
+ xs: '100%',
228
+ md: '33%',
229
+ },
230
+ },
231
+ }}>
232
+ <Box flex={1} className="payment-link-column-1" sx={{ gap: 4, display: 'flex', flexDirection: 'column' }}>
233
+ <Box className="section">
234
+ <SectionHeader title={t('admin.details')}>
235
+ <Button
236
+ variant="text"
237
+ color="inherit"
238
+ size="small"
239
+ sx={{ color: 'text.link' }}
240
+ onClick={() => setState((prev) => ({ editing: { ...prev.editing, product: true } }))}>
241
+ {t('common.edit')}
242
+ </Button>
243
+ </SectionHeader>
244
+ <Grid className="section-body" container>
245
+ <Grid
246
+ item
247
+ xs={12}
248
+ sx={{
249
+ display: 'grid',
250
+ gridTemplateColumns: {
251
+ xs: 'repeat(1, 1fr)',
252
+ sm: 'repeat(1, 1fr)',
253
+ md: 'repeat(2, 1fr)',
254
+ lg: 'repeat(3, 1fr)',
255
+ },
256
+ }}>
257
+ <InfoRow
258
+ label={t('admin.product.name.label')}
259
+ value={data.name}
260
+ direction={InfoDirection}
261
+ alignItems={InfoAlignItems}
262
+ />
263
+ <InfoRow
264
+ label={t('admin.product.description.label')}
265
+ value={data.description}
266
+ direction={InfoDirection}
267
+ alignItems={InfoAlignItems}
268
+ />
269
+ <InfoRow
270
+ label={t('admin.product.statement_descriptor.label')}
271
+ value={data.statement_descriptor}
272
+ direction={InfoDirection}
273
+ alignItems={InfoAlignItems}
274
+ />
275
+ <InfoRow
276
+ label={t('admin.product.unit_label.label')}
277
+ value={data.unit_label}
278
+ direction={InfoDirection}
279
+ alignItems={InfoAlignItems}
280
+ />
281
+ <InfoRow
282
+ label={t('admin.product.features.label')}
283
+ value={data.features.map((x) => x.name).join(',')}
284
+ direction={InfoDirection}
285
+ alignItems={InfoAlignItems}
286
+ />
287
+ <InfoRow
288
+ label={t('common.createdAt')}
289
+ value={formatTime(data.created_at)}
290
+ direction={InfoDirection}
291
+ alignItems={InfoAlignItems}
292
+ />
293
+ <InfoRow
294
+ label={t('common.updatedAt')}
295
+ value={formatTime(data.updated_at)}
296
+ direction={InfoDirection}
297
+ alignItems={InfoAlignItems}
298
+ />
299
+ <InfoRow
300
+ label={t('admin.product.image.label')}
301
+ value={
302
+ data.images.length ? (
303
+ <img
304
+ src={data.images[0]}
305
+ width={160}
306
+ height={160}
307
+ alt={data.name}
308
+ style={{ objectFit: 'cover' }}
309
+ />
310
+ ) : (
311
+ t('empty.image')
312
+ )
313
+ }
314
+ direction={InfoDirection}
315
+ alignItems={InfoAlignItems}
316
+ />
317
+ </Grid>
318
+ {state.editing.product && (
319
+ <EditProduct
320
+ product={data}
321
+ loading={state.loading.product}
322
+ onSave={onUpdateAll}
323
+ onCancel={() => setState((prev) => ({ editing: { ...prev.editing, product: false } }))}
324
+ />
325
+ )}
235
326
  </Grid>
236
- </Grid>
237
- {state.editing.metadata && (
238
- <MetadataEditor
239
- data={data}
240
- loading={state.loading.metadata}
241
- onSave={onUpdateMetadata}
242
- onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
243
- />
244
- )}
327
+ </Box>
328
+ <Divider />
329
+ <Box className="section">
330
+ <SectionHeader title={t('admin.product.pricing')}>
331
+ <Button
332
+ variant="text"
333
+ color="inherit"
334
+ size="small"
335
+ sx={{ color: 'text.link' }}
336
+ onClick={() => setState((prev) => ({ adding: { ...prev.adding, price: true } }))}>
337
+ {t('admin.price.add')}
338
+ </Button>
339
+ </SectionHeader>
340
+ <Box className="section-body">
341
+ <PricesList product={data as any} onChange={runAsync} />
342
+ {state.adding.price && (
343
+ <AddPrice
344
+ loading={state.loading.price}
345
+ onSave={onAddPrice}
346
+ onCancel={() => setState((prev) => ({ adding: { ...prev.adding, price: false } }))}
347
+ />
348
+ )}
349
+ </Box>
350
+ </Box>
351
+ <Divider />
352
+ <Box className="section">
353
+ <SectionHeader title={t('admin.events')} />
354
+ <Box className="section-body">
355
+ <EventList
356
+ features={{ toolbar: false }}
357
+ object_id={[data.id, ...data.prices.map((x) => x.id)].join(',')}
358
+ />
359
+ </Box>
360
+ </Box>
245
361
  </Box>
246
- </Box>
247
- <Box className="section">
248
- <SectionHeader title={t('admin.events')} />
249
- <Box className="section-body">
250
- <EventList features={{ toolbar: false }} object_id={[data.id, ...data.prices.map((x) => x.id)].join(',')} />
362
+ <Box className="payment-link-column-2" sx={{ gap: 3, display: 'flex', flexDirection: 'column' }}>
363
+ <Box className="section">
364
+ <SectionHeader title={t('admin.product.cross_sell.title')} />
365
+ <Box className="section-body">
366
+ <ProductCrossSell data={data} onChange={runAsync} />
367
+ </Box>
368
+ </Box>
369
+ <Divider />
370
+ <Box className="section">
371
+ <SectionHeader title={t('common.metadata.label')}>
372
+ <Button
373
+ variant="text"
374
+ color="inherit"
375
+ size="small"
376
+ sx={{ color: 'text.link' }}
377
+ disabled={state.editing.metadata}
378
+ onClick={handleEditMetadata}>
379
+ {t('common.edit')}
380
+ </Button>
381
+ </SectionHeader>
382
+ <Box className="section-body">
383
+ <MetadataList data={data.metadata} handleEditMetadata={handleEditMetadata} />
384
+ {state.editing.metadata && (
385
+ <MetadataEditor
386
+ data={data}
387
+ loading={state.loading.metadata}
388
+ onSave={onUpdateMetadata}
389
+ onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
390
+ />
391
+ )}
392
+ </Box>
393
+ </Box>
251
394
  </Box>
252
- </Box>
395
+ </Stack>
253
396
  </Root>
254
397
  );
255
398
  }
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react/no-unstable-nested-components */
2
2
  import { getDurableData } from '@arcblock/ux/lib/Datatable';
3
3
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
- import { Status, api, formatTime, usePaymentContext } from '@blocklet/payment-react';
4
+ import { Status, api, formatTime, usePaymentContext, Table } from '@blocklet/payment-react';
5
5
  import type { TProductExpanded } from '@blocklet/payment-types';
6
6
  import { CircularProgress } from '@mui/material';
7
7
  import { useEffect, useState } from 'react';
@@ -11,7 +11,6 @@ import useBus from 'use-bus';
11
11
  import FilterToolbar from '../../../../components/filter-toolbar';
12
12
  import InfoCard from '../../../../components/info-card';
13
13
  import ProductActions from '../../../../components/product/actions';
14
- import Table from '../../../../components/table';
15
14
  import { formatProductPrice } from '../../../../libs/util';
16
15
 
17
16
  const fetchData = (params: Record<string, any> = {}): Promise<{ list: TProductExpanded[]; count: number }> => {
@@ -1,6 +1,6 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
- import { Stack, Typography } from '@mui/material';
3
+ import { Box, Stack } from '@mui/material';
4
4
  import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
@@ -41,13 +41,33 @@ export default function SettingsIndex() {
41
41
 
42
42
  return (
43
43
  <>
44
- <Stack direction="row" alignItems="center" justifyContent="space-between">
45
- <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
46
- {t('admin.settings')}
47
- </Typography>
48
- {extra}
44
+ <Stack direction="row" alignItems="flex-start" justifyContent="end" flexWrap="wrap" spacing={1}>
45
+ <Tabs
46
+ tabs={tabs}
47
+ current={page}
48
+ onChange={onTabChange}
49
+ scrollButtons="auto"
50
+ variant="scrollable"
51
+ sx={{
52
+ flex: '1 0 auto',
53
+ maxWidth: '100%',
54
+ '.MuiTab-root': {
55
+ color: 'text.lighter',
56
+ },
57
+ '.MuiTabs-indicator': {
58
+ display: 'none',
59
+ },
60
+ '.Mui-selected': {
61
+ fontSize: 24,
62
+ color: 'text.primary',
63
+ },
64
+ '.MuiTabs-hideScrollbar': {
65
+ border: 'none !important',
66
+ },
67
+ }}
68
+ />
69
+ <Box> {extra}</Box>
49
70
  </Stack>
50
- <Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
51
71
  {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
52
72
  </>
53
73
  );