payment-kit 1.18.39 → 1.18.41

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 (38) hide show
  1. package/api/src/libs/subscription.ts +107 -97
  2. package/api/src/routes/checkout-sessions.ts +3 -1
  3. package/api/src/routes/connect/subscribe.ts +4 -0
  4. package/api/src/routes/subscriptions.ts +3 -1
  5. package/blocklet.yml +1 -1
  6. package/package.json +24 -24
  7. package/scripts/sdk.js +27 -1
  8. package/src/components/customer/link.tsx +6 -0
  9. package/src/components/info-card.tsx +2 -2
  10. package/src/components/info-metric.tsx +1 -1
  11. package/src/components/metadata/list.tsx +4 -2
  12. package/src/components/subscription/description.tsx +8 -6
  13. package/src/components/subscription/portal/actions.tsx +105 -36
  14. package/src/components/subscription/portal/list.tsx +152 -74
  15. package/src/components/subscription/status.tsx +17 -5
  16. package/src/locales/en.tsx +12 -7
  17. package/src/locales/zh.tsx +6 -2
  18. package/src/pages/admin/billing/invoices/detail.tsx +1 -5
  19. package/src/pages/admin/billing/subscriptions/detail.tsx +1 -5
  20. package/src/pages/admin/customers/customers/detail.tsx +1 -5
  21. package/src/pages/admin/developers/events/detail.tsx +1 -5
  22. package/src/pages/admin/developers/index.tsx +1 -1
  23. package/src/pages/admin/developers/webhooks/detail.tsx +1 -3
  24. package/src/pages/admin/overview.tsx +4 -4
  25. package/src/pages/admin/payments/intents/detail.tsx +1 -5
  26. package/src/pages/admin/payments/payouts/detail.tsx +1 -5
  27. package/src/pages/admin/payments/refunds/detail.tsx +1 -5
  28. package/src/pages/admin/products/links/detail.tsx +1 -5
  29. package/src/pages/admin/products/prices/detail.tsx +1 -5
  30. package/src/pages/admin/products/pricing-tables/detail.tsx +1 -5
  31. package/src/pages/admin/products/products/detail.tsx +1 -5
  32. package/src/pages/admin/settings/payment-methods/index.tsx +1 -1
  33. package/src/pages/customer/index.tsx +67 -138
  34. package/src/pages/customer/payout/detail.tsx +37 -49
  35. package/src/pages/customer/subscription/change-payment.tsx +2 -35
  36. package/src/pages/customer/subscription/detail.tsx +1 -5
  37. package/src/pages/integrations/donations/index.tsx +1 -1
  38. package/src/pages/integrations/overview.tsx +1 -1
@@ -22,6 +22,7 @@ import { useNavigate } from 'react-router-dom';
22
22
  import { joinURL } from 'ufo';
23
23
  import { BN } from '@ocap/util';
24
24
  import DID from '@arcblock/ux/lib/DID';
25
+ import { AddOutlined } from '@mui/icons-material';
25
26
  import CustomerCancelForm from './cancel';
26
27
  import OverdraftProtectionDialog from '../../customer/overdraft-protection';
27
28
  import Actions from '../../actions';
@@ -32,10 +33,11 @@ interface ActionConfig {
32
33
  key: string;
33
34
  show: boolean;
34
35
  label: string | ReactNode | (() => ReactNode);
36
+ labelText?: string;
35
37
  tooltip?: string;
36
38
  onClick: (e?: React.MouseEvent) => void;
37
39
  variant?: 'text' | 'outlined' | 'contained';
38
- color?: 'inherit' | 'primary' | 'secondary' | 'error';
40
+ color?: 'inherit' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
39
41
  sx?: any;
40
42
  primary?: boolean;
41
43
  component?: any;
@@ -46,8 +48,8 @@ interface ActionConfig {
46
48
 
47
49
  type ActionProps = {
48
50
  [key: string]: {
49
- color?: string;
50
- variant?: string;
51
+ color?: 'inherit' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
52
+ variant?: 'text' | 'outlined' | 'contained';
51
53
  sx?: {
52
54
  [key: string]: any;
53
55
  };
@@ -81,6 +83,10 @@ type Props = {
81
83
  actionProps?: ActionProps;
82
84
  mode?: ActionDisplayMode;
83
85
  setUp?: (methods: ActionMethods) => void;
86
+ includeActions?: string[] | null;
87
+ excludeActions?: string[] | null;
88
+ forceShowDetailAction?: boolean;
89
+ buttonSize?: 'small' | 'medium' | 'large';
84
90
  };
85
91
 
86
92
  SubscriptionActions.defaultProps = {
@@ -94,6 +100,10 @@ SubscriptionActions.defaultProps = {
94
100
  mode: 'all-buttons',
95
101
  setUp: null,
96
102
  showUnsubscribe: true,
103
+ includeActions: null,
104
+ excludeActions: null,
105
+ forceShowDetailAction: false,
106
+ buttonSize: 'small',
97
107
  };
98
108
  const fetchExtraActions = async ({
99
109
  id,
@@ -144,6 +154,10 @@ export function SubscriptionActionsInner({
144
154
  mode,
145
155
  setUp,
146
156
  showBalanceInfo,
157
+ includeActions,
158
+ excludeActions,
159
+ forceShowDetailAction,
160
+ buttonSize,
147
161
  }: Props) {
148
162
  const { t, locale } = useLocaleContext();
149
163
  const { reset, getValues } = useFormContext();
@@ -153,19 +167,29 @@ export function SubscriptionActionsInner({
153
167
  const { checkUnpaidInvoices } = useUnpaidInvoicesCheckForSubscription(subscription.id, true);
154
168
  const action = getSubscriptionAction(subs, actionProps ?? {});
155
169
 
170
+ const showAction = (actionName: string) => {
171
+ if (excludeActions && excludeActions.length > 0) {
172
+ return !excludeActions.includes(actionName);
173
+ }
174
+ if (includeActions && includeActions.length > 0) {
175
+ return includeActions.includes(actionName);
176
+ }
177
+ return true;
178
+ };
179
+
156
180
  const { data: extraActions } = useRequest(() => fetchExtraActions({ id: subscription.id, showExtra: !!showExtra }));
157
181
 
158
182
  const { data: upcoming = {} } = useRequest(
159
183
  () => api.get(`/api/subscriptions/${subscription.id}/upcoming`).then((res) => res.data),
160
184
  {
161
- ready: !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
185
+ ready: showAction('recharge') && !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
162
186
  }
163
187
  );
164
188
 
165
189
  const { data: payerValue = {} } = useRequest(
166
190
  () => api.get(`/api/subscriptions/${subscription.id}/payer-token`).then((res) => res.data),
167
191
  {
168
- ready: !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
192
+ ready: showAction('recharge') && !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
169
193
  }
170
194
  );
171
195
 
@@ -187,12 +211,13 @@ export function SubscriptionActionsInner({
187
211
  ((typeof showOverdraftProtection === 'boolean' && showOverdraftProtection) ||
188
212
  (typeof showOverdraftProtection === 'object' && showOverdraftProtection.show)) &&
189
213
  subscription?.paymentMethod?.type === 'arcblock' &&
190
- ['active', 'trialing', 'past_due'].includes(subscription?.status);
214
+ ['active', 'trialing', 'past_due'].includes(subscription?.status) &&
215
+ showAction('protection');
191
216
 
192
217
  const { data: delegation = { sufficient: true }, refresh: refreshDelegation } = useRequest(
193
218
  () => api.get(`/api/subscriptions/${subscription.id}/delegation`).then((res) => res.data),
194
219
  {
195
- ready: shouldFetchDelegation,
220
+ ready: showAction('delegation') && shouldFetchDelegation,
196
221
  refreshDeps: [subscription.id, shouldFetchDelegation],
197
222
  }
198
223
  );
@@ -359,7 +384,17 @@ export function SubscriptionActionsInner({
359
384
  const supportUnsubscribe = action?.action === 'cancel' && showUnsubscribe;
360
385
  const supportAction = action && (action?.action !== 'cancel' || supportUnsubscribe);
361
386
  const supportResume = isWillCanceled(subscription) && action?.action === 'recover';
362
- const serviceActions = subscription.service_actions?.filter((x: any) => x?.type !== 'notification') || [];
387
+ const serviceActions = subscription.service_actions?.filter((x: any) => x?.type !== 'notification') || [
388
+ {
389
+ name: 'notification',
390
+ text: {
391
+ en: 'Application Details',
392
+ zh: '应用详情',
393
+ },
394
+ link: '/customer/notification',
395
+ color: 'primary',
396
+ },
397
+ ];
363
398
  const actionConfigs: ActionConfig[] = [
364
399
  {
365
400
  key: 'delegation',
@@ -367,8 +402,8 @@ export function SubscriptionActionsInner({
367
402
  label: t('customer.delegation.btn'),
368
403
  tooltip: t('customer.delegation.title'),
369
404
  onClick: handleDelegate,
370
- variant: 'outlined',
371
- color: 'primary',
405
+ variant: actionProps?.delegation?.variant || 'outlined',
406
+ color: actionProps?.delegation?.color || 'primary',
372
407
  primary: true,
373
408
  },
374
409
  {
@@ -376,8 +411,8 @@ export function SubscriptionActionsInner({
376
411
  show: shouldFetchOverdraftProtection,
377
412
  label: t('customer.overdraftProtection.setting'),
378
413
  onClick: () => setState({ openProtection: true, protectionInitValues: null }),
379
- variant: 'outlined',
380
- color: 'primary',
414
+ variant: actionProps?.protection?.variant || 'outlined',
415
+ color: actionProps?.protection?.color || 'primary',
381
416
  },
382
417
  {
383
418
  key: 'recharge',
@@ -385,6 +420,7 @@ export function SubscriptionActionsInner({
385
420
  label: () => {
386
421
  const balanceDisplay = (
387
422
  <Stack direction="row" spacing={0.5} alignItems="center">
423
+ <AddOutlined fontSize="small" />
388
424
  {t('customer.recharge.title')}
389
425
  </Stack>
390
426
  );
@@ -417,23 +453,23 @@ export function SubscriptionActionsInner({
417
453
  )}
418
454
  <Stack spacing={0.5}>
419
455
  <Box display="flex" justifyContent="space-between">
420
- <Typography sx={{ color: 'text.secondary' }}>
456
+ <Typography sx={{ color: 'text.secondary' }} variant="body2">
421
457
  {t('admin.subscription.currentBalance')}
422
458
  </Typography>
423
- <Typography>
459
+ <Typography variant="body2">
424
460
  {formattedBalance} {subscription.paymentCurrency.symbol}
425
461
  </Typography>
426
462
  </Box>
427
463
  <Box display="flex" justifyContent="space-between">
428
- <Typography sx={{ color: 'text.secondary' }}>
464
+ <Typography sx={{ color: 'text.secondary' }} variant="body2">
429
465
  {t('admin.subscription.nextInvoiceAmount')}
430
466
  </Typography>
431
- <Typography>
467
+ <Typography variant="body2">
432
468
  {formattedUpcoming} {subscription.paymentCurrency.symbol}
433
469
  </Typography>
434
470
  </Box>
435
471
  <Box display="flex" justifyContent="space-between">
436
- <Typography sx={{ color: 'text.secondary' }}>
472
+ <Typography sx={{ color: 'text.secondary' }} variant="body2">
437
473
  {t('admin.subscription.paymentAddress')}
438
474
  </Typography>
439
475
  <DID did={payerValue?.paymentAddress} responsive={false} compact showAvatar={false} />
@@ -449,12 +485,13 @@ export function SubscriptionActionsInner({
449
485
 
450
486
  return balanceDisplay;
451
487
  },
488
+ labelText: t('customer.recharge.title'),
452
489
  onClick: (e) => {
453
490
  e?.stopPropagation();
454
491
  navigate(`/customer/subscription/${subscription.id}/recharge`);
455
492
  },
456
- variant: 'outlined',
457
- color: 'primary',
493
+ variant: actionProps?.recharge?.variant || 'outlined',
494
+ color: actionProps?.recharge?.color || 'primary',
458
495
  primary: !isWillCanceled(subscription),
459
496
  sx:
460
497
  isInsufficientBalance || payerValue.token === '0'
@@ -464,14 +501,15 @@ export function SubscriptionActionsInner({
464
501
  backgroundColor: 'error.main',
465
502
  borderRadius: '50%',
466
503
  position: 'absolute',
467
- top: '-4px',
468
- right: '-4px',
469
- width: '8px',
470
- height: '8px',
504
+ top: '3px',
505
+ right: '-3px',
506
+ width: '6px',
507
+ height: '6px',
471
508
  zIndex: 1,
472
509
  },
510
+ ...(actionProps?.recharge?.sx || {}),
473
511
  }
474
- : {},
512
+ : actionProps?.recharge?.sx || {},
475
513
  },
476
514
  {
477
515
  key: 'changePlan',
@@ -481,9 +519,9 @@ export function SubscriptionActionsInner({
481
519
  e?.stopPropagation();
482
520
  navigate(`/customer/subscription/${subscription.id}/change-plan`);
483
521
  },
484
- variant: 'contained',
485
- color: 'primary',
486
- sx: action?.sx,
522
+ variant: actionProps?.changePlan?.variant || 'contained',
523
+ color: actionProps?.changePlan?.color || 'primary',
524
+ sx: actionProps?.changePlan?.sx,
487
525
  },
488
526
  {
489
527
  key: 'batchPay',
@@ -495,9 +533,9 @@ export function SubscriptionActionsInner({
495
533
  batchPay: true,
496
534
  });
497
535
  },
498
- variant: 'outlined',
499
- color: 'error',
500
- sx: action?.sx,
536
+ variant: actionProps?.batchPay?.variant || 'outlined',
537
+ color: actionProps?.batchPay?.color || 'error',
538
+ sx: actionProps?.batchPay?.sx,
501
539
  primary: true,
502
540
  },
503
541
  {
@@ -537,15 +575,26 @@ export function SubscriptionActionsInner({
537
575
  ];
538
576
 
539
577
  // 过滤出要显示的操作
540
- const visibleActions = actionConfigs.filter((a) => a.show);
578
+ const visibleActions = actionConfigs.filter((a) => a.show && showAction(a.key));
541
579
 
542
580
  // 转换为菜单项
543
581
  const toMenuItem = (item: any) => ({
544
- label: item.label,
582
+ label: item.labelText || (typeof item.label === 'function' ? item.label() : item.label),
545
583
  handler: item.onClick,
546
584
  color: item.color,
547
585
  divider: item.divider,
548
586
  });
587
+ const detailAction = forceShowDetailAction && (
588
+ <Button
589
+ className="action-button"
590
+ key="detail"
591
+ sx={actionProps?.detail?.sx || {}}
592
+ variant={actionProps?.detail?.variant || 'outlined'}
593
+ color={actionProps?.detail?.color || 'primary'}
594
+ onClick={() => navigate(`/customer/subscription/${subscription.id}`)}>
595
+ {t('customer.subscription.manage')}
596
+ </Button>
597
+ );
549
598
 
550
599
  const toButton = (item: ActionConfig) => {
551
600
  const labelContent = typeof item.label === 'function' ? item.label() : item.label;
@@ -560,7 +609,8 @@ export function SubscriptionActionsInner({
560
609
  href={item.href}
561
610
  target={item.target}
562
611
  sx={item.sx}
563
- size="small">
612
+ className="action-button"
613
+ size={buttonSize}>
564
614
  {item.tooltip ? (
565
615
  <Tooltip title={item.tooltip}>
566
616
  <span>{labelContent}</span>
@@ -572,7 +622,14 @@ export function SubscriptionActionsInner({
572
622
  );
573
623
  };
574
624
  if (mode === 'menu-only') {
575
- return <Actions actions={visibleActions.map(toMenuItem)} variant="outlined" />;
625
+ return (
626
+ <>
627
+ {detailAction}
628
+ {visibleActions.length > 0 && (
629
+ <Actions actions={visibleActions.map(toMenuItem)} variant="outlined" sx={actionProps?.menu?.sx || {}} />
630
+ )}
631
+ </>
632
+ );
576
633
  }
577
634
 
578
635
  if (mode === 'primary-buttons') {
@@ -580,13 +637,21 @@ export function SubscriptionActionsInner({
580
637
  const menuItems = visibleActions.filter((a) => !a.primary);
581
638
  return (
582
639
  <>
640
+ {detailAction}
583
641
  {primaryButtons.map(toButton)}
584
- {menuItems.length > 0 && <Actions actions={menuItems.map(toMenuItem)} variant="outlined" />}
642
+ {menuItems.length > 0 && (
643
+ <Actions actions={menuItems.map(toMenuItem)} variant="outlined" sx={actionProps?.menu?.sx || {}} />
644
+ )}
585
645
  </>
586
646
  );
587
647
  }
588
648
 
589
- return <>{visibleActions.map(toButton)}</>;
649
+ return (
650
+ <>
651
+ {detailAction}
652
+ {visibleActions.map(toButton)}
653
+ </>
654
+ );
590
655
  };
591
656
 
592
657
  return (
@@ -682,4 +747,8 @@ SubscriptionActionsInner.defaultProps = {
682
747
  actionProps: {},
683
748
  mode: 'all-buttons',
684
749
  setUp: null,
750
+ includeActions: null,
751
+ excludeActions: null,
752
+ forceShowDetailAction: false,
753
+ buttonSize: 'small',
685
754
  };
@@ -3,7 +3,17 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Empty from '@arcblock/ux/lib/Empty';
4
4
  import { api, formatPrice, getSubscriptionTimeSummary, useMobile } from '@blocklet/payment-react';
5
5
  import type { TSubscriptionExpanded } from '@blocklet/payment-types';
6
- import { Avatar, AvatarGroup, Box, Button, CircularProgress, Stack, StackProps, Typography } from '@mui/material';
6
+ import {
7
+ Avatar,
8
+ AvatarGroup,
9
+ Box,
10
+ Button,
11
+ CircularProgress,
12
+ Divider,
13
+ Stack,
14
+ StackProps,
15
+ Typography,
16
+ } from '@mui/material';
7
17
  import { useInfiniteScroll } from 'ahooks';
8
18
 
9
19
  import { useRef } from 'react';
@@ -39,8 +49,6 @@ type Props = {
39
49
  setHasSubscriptions?: (state: boolean) => void;
40
50
  } & Omit<StackProps, 'onChange'>;
41
51
 
42
- const pageSize = 5;
43
-
44
52
  export default function CurrentSubscriptions({
45
53
  id,
46
54
  status,
@@ -52,9 +60,10 @@ export default function CurrentSubscriptions({
52
60
  setHasSubscriptions = () => {},
53
61
  ...rest
54
62
  }: Props) {
55
- const { t } = useLocaleContext();
63
+ const { t, locale } = useLocaleContext();
56
64
  const { isMobile } = useMobile();
57
65
  const listRef = useRef<HTMLDivElement | null>(null);
66
+ const pageSize = isMobile ? 5 : 10;
58
67
 
59
68
  const { data, loadMore, loadingMore, loading, reload } = useInfiniteScroll<SubscriptionListResponse>(
60
69
  (d) => {
@@ -91,54 +100,76 @@ export default function CurrentSubscriptions({
91
100
  const hasAnySubscriptions = data.totalCount > 0;
92
101
 
93
102
  const hasMore = data && data.list?.length < data.count;
94
- const size = { width: 48, height: 48 };
95
-
103
+ const size = isMobile ? { width: 42, height: 42 } : { width: 44, height: 44 };
96
104
  return (
97
- <Stack direction="column" spacing={2} sx={{ mt: 2 }}>
105
+ <Stack direction="column" spacing={2} sx={{ mt: 2, containerType: 'inline-size' }}>
98
106
  {data.list?.length > 0 ? (
99
107
  <>
100
108
  <Stack
101
109
  ref={listRef}
102
- spacing={2}
103
110
  sx={{
104
111
  maxHeight: {
105
112
  xs: '100%',
106
- md: '500px',
113
+ md: '700px',
107
114
  },
108
115
  overflowY: 'auto',
109
116
  webkitOverflowScrolling: 'touch',
117
+ display: 'grid',
118
+ gridTemplateColumns: {
119
+ xs: 'repeat(1, 1fr)',
120
+ md: 'repeat(2, 1fr)',
121
+ },
122
+ '@container (max-width: 600px)': {
123
+ gridTemplateColumns: 'repeat(1, 1fr)',
124
+ },
125
+ gap: 2,
110
126
  }}>
111
127
  {data.list.map((subscription) => {
128
+ const summary = getSubscriptionTimeSummary(subscription, locale);
129
+ const subscriptionTime = summary
130
+ ? t(`admin.subscription.${summary?.action}`, {
131
+ date: summary?.time,
132
+ prefix: summary?.isToday ? 'at' : 'on',
133
+ })
134
+ : '';
112
135
  return (
113
136
  <Stack
114
137
  key={subscription.id}
115
- direction="row"
116
- justifyContent="space-between"
117
- gap={{
118
- xs: 1,
119
- sm: 2,
120
- }}
121
138
  sx={{
122
- padding: 1.5,
123
- backgroundColor: 'grey.50',
139
+ padding: 2,
140
+ height: '100%',
141
+ borderRadius: 1,
142
+ border: '1px solid',
143
+ borderColor: 'divider',
144
+ boxShadow: 1,
145
+ display: 'flex',
146
+ flexDirection: 'column',
147
+ justifyContent: 'space-between',
148
+ gap: 2,
124
149
  '&:hover': {
125
- backgroundColor: 'grey.100',
150
+ backgroundColor: 'grey.50',
126
151
  transition: 'background-color 200ms linear',
127
152
  cursor: 'pointer',
128
153
  },
129
154
  }}
130
155
  flexWrap="wrap">
131
- <Stack direction="column" flex={1} spacing={0.5} {...rest}>
156
+ <Stack direction="column" flex={1} spacing={2} {...rest}>
132
157
  <Stack
133
- direction={isMobile ? 'column' : 'row'}
158
+ direction="row"
134
159
  spacing={1}
135
- alignItems={isMobile ? 'flex-start' : 'center'}
136
- flexWrap="wrap"
160
+ alignItems="flex-start"
137
161
  justifyContent="space-between"
138
162
  onClick={() => onClickSubscription(subscription)}>
139
- <Stack direction="row" spacing={1.5}>
140
- <AvatarGroup max={3}>
141
- {subscription.items.map((item) =>
163
+ <Stack direction="row" spacing={1.5} alignItems="center">
164
+ <AvatarGroup
165
+ max={2}
166
+ sx={{
167
+ '& .MuiAvatar-colorDefault': {
168
+ ...size,
169
+ boxSizing: 'border-box',
170
+ },
171
+ }}>
172
+ {subscription.items.slice(0, 2).map((item) =>
142
173
  item.price.product.images.length > 0 ? (
143
174
  // @ts-ignore
144
175
  <Avatar
@@ -155,15 +186,13 @@ export default function CurrentSubscriptions({
155
186
  )
156
187
  )}
157
188
  </AvatarGroup>
158
- <Stack
159
- direction="column"
160
- spacing={0.5}
161
- sx={{
162
- '.MuiTypography-body1': {
163
- fontSize: '16px',
164
- },
165
- }}>
166
- <SubscriptionDescription subscription={subscription} hideSubscription variant="body1" />
189
+ <Stack direction="column" spacing={0.25}>
190
+ <SubscriptionDescription
191
+ subscription={subscription}
192
+ hideSubscription
193
+ maxLength={isMobile ? 30 : 40}
194
+ variant={isMobile ? 'subtitle2' : 'subtitle1'}
195
+ />
167
196
  <SubscriptionStatus
168
197
  subscription={subscription}
169
198
  sx={{ height: 18, width: 'fit-content' }}
@@ -171,40 +200,40 @@ export default function CurrentSubscriptions({
171
200
  />
172
201
  </Stack>
173
202
  </Stack>
174
- <Stack>
175
- <Typography variant="subtitle1" fontWeight={500} fontSize={16}>
176
- {
177
- // @ts-ignore
178
- formatPrice(subscription.items[0].price, subscription.paymentCurrency)
179
- }
180
- </Typography>
181
- </Stack>
182
203
  </Stack>
204
+
205
+ <Divider />
183
206
  <Stack
184
207
  gap={1}
185
208
  justifyContent="space-between"
186
- flexWrap="wrap"
209
+ flexWrap="nowrap"
187
210
  sx={{
188
- flexDirection: {
189
- xs: 'column',
190
- lg: 'row',
191
- },
192
- alignItems: {
193
- xs: 'flex-start',
194
- lg: 'center',
195
- },
211
+ flexDirection: 'row',
212
+ alignItems: 'center',
196
213
  }}>
197
214
  <Box
198
215
  component="div"
199
216
  onClick={() => onClickSubscription(subscription)}
200
- sx={{ display: 'flex', gap: 0.5, flexDirection: isMobile ? 'column' : 'row' }}>
201
- {getSubscriptionTimeSummary(subscription)
202
- .split(',')
203
- .map((x) => (
204
- <Typography key={x} variant="body1" color="text.secondary">
205
- {x}
206
- </Typography>
207
- ))}
217
+ sx={{
218
+ display: 'flex',
219
+ gap: {
220
+ xs: 0.5,
221
+ md: 1,
222
+ },
223
+ flexDirection: 'row',
224
+ flexWrap: 'wrap',
225
+ }}>
226
+ <Typography variant={isMobile ? 'subtitle2' : 'subtitle1'} sx={{ whiteSpace: 'nowrap' }}>
227
+ {
228
+ // @ts-ignore
229
+ formatPrice(subscription.items[0].price, subscription.paymentCurrency)
230
+ }
231
+ </Typography>
232
+ {subscriptionTime && (
233
+ <Typography variant="body2" color="text.secondary">
234
+ ({subscriptionTime})
235
+ </Typography>
236
+ )}
208
237
  </Box>
209
238
  <SubscriptionActions
210
239
  subscription={subscription}
@@ -217,32 +246,81 @@ export default function CurrentSubscriptions({
217
246
  showUnsubscribe={false}
218
247
  showRecharge={!isWillCanceled(subscription)}
219
248
  showBalanceInfo
249
+ includeActions={['recharge']}
220
250
  actionProps={{
221
- cancel: {
222
- variant: 'outlined',
223
- color: 'primary',
224
- },
225
- recover: {
226
- variant: 'outlined',
227
- color: 'info',
228
- },
229
- pastDue: {
230
- variant: 'outlined',
251
+ recharge: {
252
+ variant: 'text',
231
253
  color: 'primary',
254
+ sx: {
255
+ whiteSpace: 'nowrap',
256
+ },
232
257
  },
233
258
  }}
234
259
  />
235
260
  </Stack>
236
261
  </Stack>
262
+ <Stack
263
+ sx={{
264
+ display: 'flex',
265
+ justifyContent: 'flex-end',
266
+ '.action-button': {
267
+ flex: 1,
268
+ },
269
+ }}>
270
+ <SubscriptionActions
271
+ subscription={subscription}
272
+ onChange={(v) => {
273
+ reload();
274
+ if (onChange) {
275
+ onChange(v);
276
+ }
277
+ }}
278
+ forceShowDetailAction
279
+ showUnsubscribe={false}
280
+ excludeActions={['recharge']}
281
+ actionProps={{
282
+ cancel: {
283
+ variant: 'outlined',
284
+ color: 'primary',
285
+ },
286
+ recover: {
287
+ variant: 'outlined',
288
+ color: 'info',
289
+ },
290
+ pastDue: {
291
+ variant: 'outlined',
292
+ color: 'primary',
293
+ },
294
+ detail: {
295
+ variant: 'contained',
296
+ sx: {
297
+ color: 'text.primary',
298
+
299
+ backgroundColor: 'grey.100',
300
+ '&:hover': {
301
+ backgroundColor: 'grey.200',
302
+ },
303
+ },
304
+ },
305
+ menu: {
306
+ sx: {
307
+ color: 'text.primary',
308
+ },
309
+ },
310
+ }}
311
+ mode="menu-only"
312
+ buttonSize="medium"
313
+ />
314
+ </Stack>
237
315
  </Stack>
238
316
  );
239
317
  })}
240
- {hasMore && !isMobile && showLoadingMore && (
241
- <Box alignItems="center" gap={0.5} display="flex" mt={0.5}>
242
- {t('common.loadingMore', { resource: t('admin.subscriptions') })}
243
- </Box>
244
- )}
245
318
  </Stack>
319
+ {hasMore && !isMobile && showLoadingMore && (
320
+ <Box alignItems="center" gap={0.5} display="flex" mt={0.5}>
321
+ {t('common.loadingMore', { resource: t('admin.subscriptions') })}
322
+ </Box>
323
+ )}
246
324
  {isMobile && (
247
325
  <Box>
248
326
  {hasMore && (