payment-kit 1.13.165 → 1.13.166

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/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.13.165
17
+ version: 1.13.166
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.165",
3
+ "version": "1.13.166",
4
4
  "scripts": {
5
5
  "dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -50,7 +50,7 @@
50
50
  "@arcblock/jwt": "^1.18.110",
51
51
  "@arcblock/ux": "^2.9.39",
52
52
  "@blocklet/logger": "1.16.23",
53
- "@blocklet/payment-react": "1.13.165",
53
+ "@blocklet/payment-react": "1.13.166",
54
54
  "@blocklet/sdk": "1.16.23",
55
55
  "@blocklet/ui-react": "^2.9.39",
56
56
  "@blocklet/uploader": "^0.0.74",
@@ -110,7 +110,7 @@
110
110
  "devDependencies": {
111
111
  "@abtnode/types": "1.16.23",
112
112
  "@arcblock/eslint-config-ts": "^0.2.4",
113
- "@blocklet/payment-types": "1.13.165",
113
+ "@blocklet/payment-types": "1.13.166",
114
114
  "@types/cookie-parser": "^1.4.6",
115
115
  "@types/cors": "^2.8.17",
116
116
  "@types/dotenv-flow": "^3.3.3",
@@ -149,5 +149,5 @@
149
149
  "parser": "typescript"
150
150
  }
151
151
  },
152
- "gitHead": "24fadab6620ca13856393399efd6a1dffc431d66"
152
+ "gitHead": "70ee269e307cbab0ecb58fb4f287b749df0f2277"
153
153
  }
package/src/app.tsx CHANGED
@@ -13,6 +13,7 @@ import { joinURL } from 'ufo';
13
13
 
14
14
  import ErrorFallback from './components/error-fallback';
15
15
  import Layout from './components/layout/admin';
16
+ import { TransitionProvider } from './components/progress-bar';
16
17
  import { SessionProvider } from './contexts/session';
17
18
  import { translations } from './locales';
18
19
 
@@ -38,82 +39,84 @@ const theme = createTheme({
38
39
  function App() {
39
40
  return (
40
41
  <ThemeProvider theme={theme}>
41
- <LocaleProvider translations={translations} fallbackLocale="en">
42
- <ErrorBoundary FallbackComponent={ErrorFallback} onReset={window.location.reload}>
43
- <Suspense
44
- fallback={
45
- <Center>
46
- <CircularProgress />
47
- </Center>
48
- }>
49
- <Routes>
50
- <Route path="/" element={<HomePage />} />
51
- <Route path="/checkout/:action/:id" element={<CheckoutPage />} />
52
- <Route key="admin-index" path="/admin" element={<AdminPage />} />,
53
- <Route key="admin-tabs" path="/admin/:group" element={<AdminPage />} />,
54
- <Route key="admin-sub" path="/admin/:group/:page" element={<AdminPage />} />,
55
- <Route key="admin-fallback" path="/admin/*" element={<AdminPage />} />,
56
- <Route
57
- key="customer-home"
58
- path="/customer"
59
- element={
60
- <Layout>
61
- <CustomerHome />
62
- </Layout>
63
- }
64
- />
65
- <Route
66
- key="customer-subscription"
67
- path="/customer/subscription/:id"
68
- element={
69
- <Layout>
70
- <CustomerSubscriptionDetail />
71
- </Layout>
72
- }
73
- />
74
- <Route
75
- key="customer-subscription-change-plan"
76
- path="/customer/subscription/:id/change-plan"
77
- element={
78
- <Layout>
79
- <CustomerSubscriptionChangePlan />
80
- </Layout>
81
- }
82
- />
83
- <Route
84
- key="customer-subscription-change-payment"
85
- path="/customer/subscription/:id/change-payment"
86
- element={
87
- <Layout>
88
- <CustomerSubscriptionChangePayment />
89
- </Layout>
90
- }
91
- />
92
- <Route key="subscription-embed" path="/customer/embed/subscription" element={<MiniInvoiceList />} />,
93
- <Route
94
- key="customer-due"
95
- path="/customer/invoice/past-due"
96
- element={
97
- <Layout>
98
- <CustomerInvoicePastDue />
99
- </Layout>
100
- }
101
- />
102
- <Route
103
- key="customer-invoice"
104
- path="/customer/invoice/:id"
105
- element={
106
- <Layout>
107
- <CustomerInvoiceDetail />
108
- </Layout>
109
- }
110
- />
111
- <Route key="customer-fallback" path="/customer/*" element={<Navigate to="/customer" />} />,
112
- <Route path="*" element={<Navigate to="/" />} />
113
- </Routes>
114
- </Suspense>
115
- </ErrorBoundary>
116
- </LocaleProvider>
42
+ <TransitionProvider>
43
+ <LocaleProvider translations={translations} fallbackLocale="en">
44
+ <ErrorBoundary FallbackComponent={ErrorFallback} onReset={window.location.reload}>
45
+ <Suspense
46
+ fallback={
47
+ <Center>
48
+ <CircularProgress />
49
+ </Center>
50
+ }>
51
+ <Routes>
52
+ <Route path="/" element={<HomePage />} />
53
+ <Route path="/checkout/:action/:id" element={<CheckoutPage />} />
54
+ <Route key="admin-index" path="/admin" element={<AdminPage />} />,
55
+ <Route key="admin-tabs" path="/admin/:group" element={<AdminPage />} />,
56
+ <Route key="admin-sub" path="/admin/:group/:page" element={<AdminPage />} />,
57
+ <Route key="admin-fallback" path="/admin/*" element={<AdminPage />} />,
58
+ <Route
59
+ key="customer-home"
60
+ path="/customer"
61
+ element={
62
+ <Layout>
63
+ <CustomerHome />
64
+ </Layout>
65
+ }
66
+ />
67
+ <Route
68
+ key="customer-subscription"
69
+ path="/customer/subscription/:id"
70
+ element={
71
+ <Layout>
72
+ <CustomerSubscriptionDetail />
73
+ </Layout>
74
+ }
75
+ />
76
+ <Route
77
+ key="customer-subscription-change-plan"
78
+ path="/customer/subscription/:id/change-plan"
79
+ element={
80
+ <Layout>
81
+ <CustomerSubscriptionChangePlan />
82
+ </Layout>
83
+ }
84
+ />
85
+ <Route
86
+ key="customer-subscription-change-payment"
87
+ path="/customer/subscription/:id/change-payment"
88
+ element={
89
+ <Layout>
90
+ <CustomerSubscriptionChangePayment />
91
+ </Layout>
92
+ }
93
+ />
94
+ <Route key="subscription-embed" path="/customer/embed/subscription" element={<MiniInvoiceList />} />,
95
+ <Route
96
+ key="customer-due"
97
+ path="/customer/invoice/past-due"
98
+ element={
99
+ <Layout>
100
+ <CustomerInvoicePastDue />
101
+ </Layout>
102
+ }
103
+ />
104
+ <Route
105
+ key="customer-invoice"
106
+ path="/customer/invoice/:id"
107
+ element={
108
+ <Layout>
109
+ <CustomerInvoiceDetail />
110
+ </Layout>
111
+ }
112
+ />
113
+ <Route key="customer-fallback" path="/customer/*" element={<Navigate to="/customer" />} />,
114
+ <Route path="*" element={<Navigate to="/" />} />
115
+ </Routes>
116
+ </Suspense>
117
+ </ErrorBoundary>
118
+ </LocaleProvider>
119
+ </TransitionProvider>
117
120
  </ThemeProvider>
118
121
  );
119
122
  }
@@ -8,6 +8,7 @@ import { useRequest } from 'ahooks';
8
8
  import { useEffect, useState } from 'react';
9
9
  import { useNavigate } from 'react-router-dom';
10
10
 
11
+ import { useTransitionContext } from '../progress-bar';
11
12
  import Table from '../table';
12
13
 
13
14
  const fetchData = (params: Record<string, any> = {}): Promise<{ list: TEventExpanded[]; count: number }> => {
@@ -141,6 +142,7 @@ export default function EventList({ type, object_id, features }: ListProps) {
141
142
  pageSize: persisted.rowsPerPage || 50,
142
143
  page: persisted.page ? persisted.page + 1 : 1,
143
144
  });
145
+ const { startTransition } = useTransitionContext();
144
146
 
145
147
  const { loading, error, data, refresh } = useRequest(() => fetchData(search));
146
148
  useEffect(() => {
@@ -207,7 +209,9 @@ export default function EventList({ type, object_id, features }: ListProps) {
207
209
  rowsPerPage: search.pageSize,
208
210
  onRowClick: (_: any, { dataIndex }: any) => {
209
211
  const item = data.list[dataIndex] as TEventExpanded;
210
- navigate(`/admin/developers/${item.id}`);
212
+ startTransition(() => {
213
+ navigate(`/admin/developers/${item.id}`);
214
+ });
211
215
  },
212
216
  }}
213
217
  toolbar={features?.toolbar}
@@ -2,7 +2,7 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import { api, usePaymentContext } from '@blocklet/payment-react';
3
3
  import type { TCustomer } from '@blocklet/payment-types';
4
4
  import { Add, Close } from '@mui/icons-material';
5
- import { ClickAwayListener, Menu, MenuItem } from '@mui/material';
5
+ import { Button, ClickAwayListener, Menu, MenuItem } from '@mui/material';
6
6
  import { Box, styled } from '@mui/system';
7
7
  import { useEffect, useState } from 'react';
8
8
 
@@ -86,7 +86,7 @@ function SearchStatus({ status = [], setSearch }: Pick<Props, 'status' | 'setSea
86
86
  onClick={(e) => {
87
87
  setShow(e.currentTarget as any);
88
88
  }}>
89
- <div className="option-btn">
89
+ <Button className="option-btn" variant="text">
90
90
  {display ? (
91
91
  <Close
92
92
  sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
@@ -104,7 +104,7 @@ function SearchStatus({ status = [], setSearch }: Pick<Props, 'status' | 'setSea
104
104
  )}
105
105
  {t('common.status')}
106
106
  <span>{display}</span>
107
- </div>
107
+ </Button>
108
108
  <Menu
109
109
  anchorEl={show}
110
110
  open={Boolean(show)}
@@ -155,7 +155,7 @@ function SearchCurrency({ setSearch }: Pick<Props, 'setSearch'>) {
155
155
  onClick={(e) => {
156
156
  setShow(e.currentTarget as any);
157
157
  }}>
158
- <div className="option-btn">
158
+ <Button className="option-btn" variant="text">
159
159
  {display ? (
160
160
  <Close
161
161
  sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
@@ -173,7 +173,7 @@ function SearchCurrency({ setSearch }: Pick<Props, 'setSearch'>) {
173
173
  )}
174
174
  {t('common.currency')}
175
175
  <span>{display}</span>
176
- </div>
176
+ </Button>
177
177
  <Menu
178
178
  anchorEl={show}
179
179
  open={Boolean(show)}
@@ -226,7 +226,7 @@ function SearchCustomers({ setSearch }: Pick<Props, 'setSearch'>) {
226
226
 
227
227
  return (
228
228
  <section onClick={(e: any) => setShow(e.currentTarget)}>
229
- <div className="option-btn">
229
+ <Button className="option-btn" variant="text">
230
230
  {display ? (
231
231
  <Close
232
232
  sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
@@ -244,7 +244,7 @@ function SearchCustomers({ setSearch }: Pick<Props, 'setSearch'>) {
244
244
  )}
245
245
  {t('common.customer')}
246
246
  <span>{display}</span>
247
- </div>
247
+ </Button>
248
248
 
249
249
  <Menu
250
250
  anchorEl={show}
@@ -327,7 +327,7 @@ function SearchProducts({ setSearch }: Pick<Props, 'setSearch'>) {
327
327
  setShow(false);
328
328
  }}>
329
329
  <section onClick={() => setShow(!show)}>
330
- <div className="option-btn">
330
+ <Button className="option-btn" variant="text">
331
331
  {display ? (
332
332
  <Close
333
333
  sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
@@ -345,7 +345,7 @@ function SearchProducts({ setSearch }: Pick<Props, 'setSearch'>) {
345
345
  )}
346
346
  {t('admin.subscription.product')}
347
347
  <span>{display}</span>
348
- </div>
348
+ </Button>
349
349
 
350
350
  <ul
351
351
  className="status-options"
@@ -388,7 +388,11 @@ const Root = styled(Box)`
388
388
  align-items: center;
389
389
  border-radius: 25px;
390
390
  background: #f6f6f6;
391
- padding: 0 10px;
391
+ padding: 5px 10px;
392
+ color: #555;
393
+ font-size: 14px;
394
+ line-height: 14px;
395
+ overflow: hidden;
392
396
  }
393
397
 
394
398
  .option-btn span {
@@ -10,6 +10,7 @@ import { useNavigate } from 'react-router-dom';
10
10
 
11
11
  import CustomerLink from '../customer/link';
12
12
  import FilterToolbar from '../filter-toolbar';
13
+ import { useTransitionContext } from '../progress-bar';
13
14
  import Table from '../table';
14
15
  import InvoiceActions from './action';
15
16
 
@@ -93,6 +94,7 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
93
94
  pageSize: persisted.rowsPerPage || 20,
94
95
  page: persisted.page ? persisted.page + 1 : 1,
95
96
  });
97
+ const { startTransition } = useTransitionContext();
96
98
 
97
99
  const [data, setData] = useState({}) as any;
98
100
 
@@ -228,7 +230,7 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
228
230
  if (invoiceProps?.onClick) {
229
231
  await invoiceProps.onClick(item);
230
232
  } else {
231
- navigate(`/admin/billing/${item.id}`);
233
+ startTransition(() => navigate(`/admin/billing/${item.id}`));
232
234
  }
233
235
  },
234
236
  onColumnSortChange(_: any, order: any) {
@@ -11,6 +11,7 @@ import { useNavigate } from 'react-router-dom';
11
11
  import { debounce } from '../../libs/util';
12
12
  import CustomerLink from '../customer/link';
13
13
  import FilterToolbar from '../filter-toolbar';
14
+ import { useTransitionContext } from '../progress-bar';
14
15
  import Table from '../table';
15
16
  import PaymentIntentActions from './actions';
16
17
 
@@ -84,6 +85,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
84
85
  page: persisted.page ? persisted.page + 1 : 1,
85
86
  });
86
87
 
88
+ const { startTransition } = useTransitionContext();
87
89
  const [data, setData] = useState({}) as any;
88
90
 
89
91
  useEffect(() => {
@@ -239,7 +241,9 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
239
241
  },
240
242
  onRowClick: (_: any, { dataIndex }: any) => {
241
243
  const item = data.list[dataIndex] as TPaymentIntentExpanded;
242
- navigate(`/admin/payments/${item.id}`);
244
+ startTransition(() => {
245
+ navigate(`/admin/payments/${item.id}`);
246
+ });
243
247
  },
244
248
  }}
245
249
  toolbar={features?.toolbar}
@@ -0,0 +1,56 @@
1
+ import Box from '@mui/material/Box';
2
+ import LinearProgress from '@mui/material/LinearProgress';
3
+ import * as React from 'react';
4
+
5
+ type Props = {
6
+ pending: boolean;
7
+ };
8
+
9
+ export default function ProgressBar({ pending = true }: Props) {
10
+ const [progress, setProgress] = React.useState(0);
11
+ React.useEffect(() => {
12
+ const timer = setInterval(
13
+ () => {
14
+ setProgress((oldProgress) => {
15
+ if (oldProgress === 100) {
16
+ if (timer) {
17
+ clearInterval(timer);
18
+ }
19
+ return -1;
20
+ }
21
+ const diff = Math.random() * 10;
22
+ return Math.min(oldProgress + diff, 100);
23
+ });
24
+ },
25
+ pending ? 500 : 30
26
+ ); // 如果已完成,就加速
27
+
28
+ return () => {
29
+ clearInterval(timer);
30
+ };
31
+ }, [pending]);
32
+ return (
33
+ <Box sx={{ width: '100%', height: '4px', position: 'fixed', top: 0, left: 0, zIndex: '999999' }}>
34
+ <LinearProgress
35
+ variant="determinate"
36
+ value={progress}
37
+ sx={{ opacity: progress === -1 ? 0 : 1, transition: '0.3s' }}
38
+ />
39
+ </Box>
40
+ );
41
+ }
42
+
43
+ const Transition = React.createContext({
44
+ startTransition: null,
45
+ } as any);
46
+
47
+ export function TransitionProvider({ children }: { children: React.ReactNode }) {
48
+ const [isPending, startTransition] = React.useTransition();
49
+
50
+ const memoObj = React.useMemo(() => ({ isPending, startTransition }), [isPending]);
51
+ return <Transition.Provider value={memoObj}>{children}</Transition.Provider>;
52
+ }
53
+
54
+ export function useTransitionContext() {
55
+ return React.useContext(Transition);
56
+ }
@@ -10,6 +10,7 @@ import { useNavigate } from 'react-router-dom';
10
10
 
11
11
  import CustomerLink from '../customer/link';
12
12
  import FilterToolbar from '../filter-toolbar';
13
+ import { useTransitionContext } from '../progress-bar';
13
14
  import Table from '../table';
14
15
  import RefundActions from './actions';
15
16
 
@@ -74,7 +75,7 @@ RefundList.defaultProps = {
74
75
  export default function RefundList({ customer_id, invoice_id, subscription_id, features }: ListProps) {
75
76
  const { t } = useLocaleContext();
76
77
  const navigate = useNavigate();
77
-
78
+ const { startTransition } = useTransitionContext();
78
79
  const listKey = getListKey({ customer_id, invoice_id });
79
80
 
80
81
  const persisted = getDurableData(listKey);
@@ -199,7 +200,9 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, f
199
200
  rowsPerPage: search.pageSize,
200
201
  onRowClick: (_: any, { dataIndex }: any) => {
201
202
  const item = data.list[dataIndex] as TRefundExpanded;
202
- navigate(`/admin/payments/${item.id}`);
203
+ startTransition(() => {
204
+ navigate(`/admin/payments/${item.id}`);
205
+ });
203
206
  },
204
207
  onColumnSortChange(_: any, order: any) {
205
208
  setSearch({
@@ -9,6 +9,7 @@ import { useNavigate } from 'react-router-dom';
9
9
 
10
10
  import CustomerLink from '../customer/link';
11
11
  import FilterToolbar from '../filter-toolbar';
12
+ import { useTransitionContext } from '../progress-bar';
12
13
  import Table from '../table';
13
14
  import SubscriptionActions from './actions';
14
15
  import SubscriptionDescription from './description';
@@ -71,6 +72,7 @@ export default function SubscriptionList({ customer_id, features, status }: List
71
72
 
72
73
  const { t } = useLocaleContext();
73
74
  const navigate = useNavigate();
75
+ const { startTransition } = useTransitionContext();
74
76
  const [search, setSearch] = useState<SearchProps>({
75
77
  status: status as string,
76
78
  customer_id,
@@ -192,7 +194,9 @@ export default function SubscriptionList({ customer_id, features, status }: List
192
194
  rowsPerPage: search.pageSize,
193
195
  onRowClick: (_: any, { dataIndex }: any) => {
194
196
  const item = data.list[dataIndex] as TSubscriptionExpanded;
195
- navigate(`/admin/billing/${item.id}`);
197
+ startTransition(() => {
198
+ navigate(`/admin/billing/${item.id}`);
199
+ });
196
200
  },
197
201
  onColumnSortChange(_: any, order: any) {
198
202
  setSearch({
@@ -1,9 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
3
  import { Typography } from '@mui/material';
4
- import React, { isValidElement, startTransition } from 'react';
4
+ import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const SubscriptionDetail = React.lazy(() => import('./subscriptions/detail'));
8
10
  const InvoiceDetail = React.lazy(() => import('./invoices/detail'));
9
11
 
@@ -16,6 +18,7 @@ export default function BillingIndex() {
16
18
  const navigate = useNavigate();
17
19
  const { t } = useLocaleContext();
18
20
  const { page = 'invoices' } = useParams();
21
+ const { startTransition } = useTransitionContext();
19
22
 
20
23
  if (page.startsWith('sub_')) {
21
24
  return <SubscriptionDetail id={page} />;
@@ -8,6 +8,7 @@ import { Avatar, CircularProgress, Stack, Typography } from '@mui/material';
8
8
  import { useEffect, useState } from 'react';
9
9
  import { useNavigate } from 'react-router-dom';
10
10
 
11
+ import { useTransitionContext } from '../../../../components/progress-bar';
11
12
  import Table from '../../../../components/table';
12
13
 
13
14
  const fetchData = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
@@ -30,6 +31,7 @@ export default function CustomersList() {
30
31
 
31
32
  const { t } = useLocaleContext();
32
33
  const navigate = useNavigate();
34
+ const { startTransition } = useTransitionContext();
33
35
  const [search, setSearch] = useState<{ active?: string; pageSize: number; page: number; q?: any; o?: string }>({
34
36
  active: '',
35
37
  o: 'desc',
@@ -118,7 +120,9 @@ export default function CustomersList() {
118
120
  rowsPerPage: search.pageSize,
119
121
  onRowClick: (_: any, { dataIndex }: any) => {
120
122
  const item = data.list[dataIndex] as TCustomer;
121
- navigate(`/admin/customers/${item.id}`);
123
+ startTransition(() => {
124
+ navigate(`/admin/customers/${item.id}`);
125
+ });
122
126
  },
123
127
  onColumnSortChange(_: any, order: any) {
124
128
  setSearch({
@@ -1,9 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
3
  import { Typography } from '@mui/material';
4
- import React, { isValidElement, startTransition } from 'react';
4
+ import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const CustomerDetail = React.lazy(() => import('./customers/detail'));
8
10
 
9
11
  const pages = {
@@ -14,6 +16,7 @@ export default function CustomerIndex() {
14
16
  const navigate = useNavigate();
15
17
  const { t } = useLocaleContext();
16
18
  const { page = 'overview' } = useParams();
19
+ const { startTransition } = useTransitionContext();
17
20
 
18
21
  if (page.startsWith('cus_')) {
19
22
  return <CustomerDetail id={page} />;
@@ -1,9 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
3
  import { Typography } from '@mui/material';
4
- import React, { isValidElement, startTransition } from 'react';
4
+ import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const EventDetail = React.lazy(() => import('./events/detail'));
8
10
  const WebhookDetail = React.lazy(() => import('./webhooks/detail'));
9
11
 
@@ -18,6 +20,7 @@ export default function DevelopersIndex() {
18
20
  const navigate = useNavigate();
19
21
  const { t } = useLocaleContext();
20
22
  const { page = 'overview' } = useParams();
23
+ const { startTransition } = useTransitionContext();
21
24
 
22
25
  if (page.startsWith('evt_')) {
23
26
  return <EventDetail id={page} />;
@@ -8,6 +8,7 @@ import { useRequest } from 'ahooks';
8
8
  import { useEffect, useState } from 'react';
9
9
  import { useNavigate } from 'react-router-dom';
10
10
 
11
+ import { useTransitionContext } from '../../../../components/progress-bar';
11
12
  import Table from '../../../../components/table';
12
13
  import { getWebhookStatusColor } from '../../../../libs/util';
13
14
 
@@ -25,6 +26,7 @@ export default function WebhooksList() {
25
26
 
26
27
  const { t } = useLocaleContext();
27
28
  const navigate = useNavigate();
29
+ const { startTransition } = useTransitionContext();
28
30
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
29
31
  active: '',
30
32
  pageSize: persisted.rowsPerPage || 20,
@@ -90,7 +92,9 @@ export default function WebhooksList() {
90
92
  rowsPerPage: search.pageSize,
91
93
  onRowClick: (_: any, { dataIndex }: any) => {
92
94
  const item = data.list[dataIndex] as TWebhookEndpoint;
93
- navigate(`/admin/developers/${item.id}`);
95
+ startTransition(() => {
96
+ navigate(`/admin/developers/${item.id}`);
97
+ });
94
98
  },
95
99
  }}
96
100
  loading={loading}
@@ -1,10 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import { PaymentProvider, Switch, usePaymentContext } from '@blocklet/payment-react';
3
3
  import { Box, Chip, Stack } from '@mui/material';
4
- import React, { Suspense, isValidElement, startTransition, useEffect } from 'react';
4
+ import React, { isValidElement, useEffect } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
7
  import Layout from '../../components/layout/admin';
8
+ import ProgressBar, { useTransitionContext } from '../../components/progress-bar';
8
9
  import { useSessionContext } from '../../contexts/session';
9
10
 
10
11
  const groups = {
@@ -43,6 +44,7 @@ function Admin() {
43
44
  const { t } = useLocaleContext();
44
45
  const settings = usePaymentContext();
45
46
  const { group = 'overview' } = useParams();
47
+ const { isPending, startTransition } = useTransitionContext();
46
48
 
47
49
  const onLivemodeChange = (e: any) => {
48
50
  settings.setLivemode(!e.target.checked);
@@ -71,6 +73,7 @@ function Admin() {
71
73
 
72
74
  return (
73
75
  <Layout>
76
+ <ProgressBar pending={isPending} />
74
77
  <Stack
75
78
  direction="row"
76
79
  alignItems="center"
@@ -99,9 +102,7 @@ function Admin() {
99
102
  </label>
100
103
  </Stack>
101
104
  </Stack>
102
- <Suspense fallback={<div />}>
103
- <div className="page-content">{isValidElement(TabComponent) ? TabComponent : <TabComponent />}</div>
104
- </Suspense>
105
+ <div className="page-content">{isValidElement(TabComponent) ? TabComponent : <TabComponent />}</div>
105
106
  </Layout>
106
107
  );
107
108
  }
@@ -1,9 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
3
  import { Stack, Typography } from '@mui/material';
4
- import React, { isValidElement, startTransition } from 'react';
4
+ import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const PaymentLinkCreate = React.lazy(() => import('./links/create'));
8
10
  const PaymentLinkDetail = React.lazy(() => import('./links/detail'));
9
11
  const PaymentIntentDetail = React.lazy(() => import('./intents/detail'));
@@ -19,6 +21,7 @@ export default function PaymentIndex() {
19
21
  const navigate = useNavigate();
20
22
  const { t } = useLocaleContext();
21
23
  const { page = 'intents' } = useParams();
24
+ const { startTransition } = useTransitionContext();
22
25
 
23
26
  if (page.startsWith('pi_')) {
24
27
  return <PaymentIntentDetail id={page} />;
@@ -12,6 +12,7 @@ import useBus from 'use-bus';
12
12
 
13
13
  import Copyable from '../../../../components/copyable';
14
14
  import PaymentLinkActions from '../../../../components/payment-link/actions';
15
+ import { useTransitionContext } from '../../../../components/progress-bar';
15
16
  import Table from '../../../../components/table';
16
17
  import { ProductsProvider } from '../../../../contexts/products';
17
18
  import { formatPaymentLinkPricing } from '../../../../libs/util';
@@ -29,6 +30,7 @@ function PaymentLinks() {
29
30
  const { t } = useLocaleContext();
30
31
  const { settings } = usePaymentContext();
31
32
  const navigate = useNavigate();
33
+ const { startTransition } = useTransitionContext();
32
34
 
33
35
  const persisted = getDurableData(listKey);
34
36
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
@@ -148,7 +150,9 @@ function PaymentLinks() {
148
150
  rowsPerPage: search.pageSize,
149
151
  onRowClick: (_: any, { dataIndex }: any) => {
150
152
  const item = data.list[dataIndex] as TPaymentLinkExpanded;
151
- navigate(`/admin/payments/${item.id}`);
153
+ startTransition(() => {
154
+ navigate(`/admin/payments/${item.id}`);
155
+ });
152
156
  },
153
157
  }}
154
158
  loading={loading}
@@ -4,6 +4,8 @@ import { Stack, Typography } from '@mui/material';
4
4
  import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const ProductCreate = React.lazy(() => import('./products/create'));
8
10
  const ProductDetail = React.lazy(() => import('./products/detail'));
9
11
  const PriceDetail = React.lazy(() => import('./prices/detail'));
@@ -21,6 +23,7 @@ export default function Products() {
21
23
  const navigate = useNavigate();
22
24
  const { t } = useLocaleContext();
23
25
  const { page = 'products' } = useParams();
26
+ const { startTransition } = useTransitionContext();
24
27
 
25
28
  if (page.startsWith('prod_')) {
26
29
  return <ProductDetail id={page} />;
@@ -61,7 +64,7 @@ export default function Products() {
61
64
  <Tabs
62
65
  tabs={tabs}
63
66
  current={page}
64
- onChange={(newTab: string) => navigate(`/admin/products/${newTab}`)}
67
+ onChange={(newTab: string) => startTransition(() => navigate(`/admin/products/${newTab}`))}
65
68
  scrollButtons="auto"
66
69
  />
67
70
  {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
@@ -12,6 +12,7 @@ import useBus from 'use-bus';
12
12
 
13
13
  import Copyable from '../../../../components/copyable';
14
14
  import PricingTableActions from '../../../../components/pricing-table/actions';
15
+ import { useTransitionContext } from '../../../../components/progress-bar';
15
16
  import Table from '../../../../components/table';
16
17
  import { ProductsProvider } from '../../../../contexts/products';
17
18
 
@@ -27,6 +28,7 @@ function PricingTables() {
27
28
  const listKey = 'pricing-tables';
28
29
  const { t } = useLocaleContext();
29
30
  const navigate = useNavigate();
31
+ const { startTransition } = useTransitionContext();
30
32
 
31
33
  const persisted = getDurableData(listKey);
32
34
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
@@ -135,7 +137,9 @@ function PricingTables() {
135
137
  rowsPerPage: search.pageSize,
136
138
  onRowClick: (_: any, { dataIndex }: any) => {
137
139
  const item = data.list[dataIndex] as TPricingTableExpanded;
138
- navigate(`/admin/products/${item.id}`);
140
+ startTransition(() => {
141
+ navigate(`/admin/products/${item.id}`);
142
+ });
139
143
  },
140
144
  }}
141
145
  loading={loading}
@@ -7,9 +7,10 @@ import { CircularProgress } from '@mui/material';
7
7
  import { useEffect, useState } from 'react';
8
8
  import { useNavigate } from 'react-router-dom';
9
9
 
10
- import FilterTooolbar from '../../../../components/filter-toolbar';
10
+ import FilterToolbar from '../../../../components/filter-toolbar';
11
11
  import InfoCard from '../../../../components/info-card';
12
12
  import ProductActions from '../../../../components/product/actions';
13
+ import { useTransitionContext } from '../../../../components/progress-bar';
13
14
  import Table from '../../../../components/table';
14
15
  import { formatProductPrice } from '../../../../libs/util';
15
16
 
@@ -33,6 +34,7 @@ export default function ProductsList() {
33
34
 
34
35
  const { t, locale } = useLocaleContext();
35
36
  const navigate = useNavigate();
37
+ const { startTransition } = useTransitionContext();
36
38
  const { settings } = usePaymentContext();
37
39
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: any; q?: any; o?: any }>({
38
40
  active: '',
@@ -130,7 +132,7 @@ export default function ProductsList() {
130
132
  <Table
131
133
  durable={listKey}
132
134
  durableKeys={['page', 'rowsPerPage']}
133
- title={<FilterTooolbar setSearch={setSearch} search={search} status={['active', 'archived']} />}
135
+ title={<FilterToolbar setSearch={setSearch} search={search} status={['active', 'archived']} />}
134
136
  data={data.list}
135
137
  columns={columns}
136
138
  options={{
@@ -165,7 +167,9 @@ export default function ProductsList() {
165
167
  },
166
168
  onRowClick: (_: any, { dataIndex }: any) => {
167
169
  const item = data.list[dataIndex] as TProductExpanded;
168
- navigate(`/admin/products/${item.id}`);
170
+ startTransition(() => {
171
+ navigate(`/admin/products/${item.id}`);
172
+ });
169
173
  },
170
174
  }}
171
175
  loading={!data.list}
@@ -1,9 +1,11 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Tabs from '@arcblock/ux/lib/Tabs';
3
3
  import { Stack, Typography } from '@mui/material';
4
- import React, { isValidElement, startTransition } from 'react';
4
+ import React, { isValidElement } from 'react';
5
5
  import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
+ import { useTransitionContext } from '../../../components/progress-bar';
8
+
7
9
  const PaymentMethodCreate = React.lazy(() => import('./payment-methods/create'));
8
10
 
9
11
  const pages = {
@@ -16,6 +18,7 @@ export default function SettingsIndex() {
16
18
  const navigate = useNavigate();
17
19
  const { t } = useLocaleContext();
18
20
  const { page = 'payment-methods' } = useParams();
21
+ const { startTransition } = useTransitionContext();
19
22
 
20
23
  const onTabChange = (newTab: string) => {
21
24
  startTransition(() => {
@@ -17,6 +17,7 @@ import BalanceList from '../../components/balance-list';
17
17
  import EditCustomer from '../../components/customer/edit';
18
18
  import InfoMetric from '../../components/info-metric';
19
19
  import InfoRow from '../../components/info-row';
20
+ import ProgressBar, { useTransitionContext } from '../../components/progress-bar';
20
21
  import SectionHeader from '../../components/section/header';
21
22
  import CurrentSubscriptions from '../../components/subscription/portal/list';
22
23
  import { useSessionContext } from '../../contexts/session';
@@ -31,6 +32,7 @@ export default function CustomerHome() {
31
32
  const { events, session, connectApi } = useSessionContext();
32
33
  const [state, setState] = useSetState({ editing: false, loading: false });
33
34
  const navigate = useNavigate();
35
+ const { isPending, startTransition } = useTransitionContext();
34
36
 
35
37
  const { loading, error, data, runAsync } = useRequest(fetchData);
36
38
 
@@ -71,115 +73,120 @@ export default function CustomerHome() {
71
73
  };
72
74
 
73
75
  return (
74
- <Grid container spacing={5}>
75
- <Grid item xs={12} md={8}>
76
- <Root direction="column" spacing={3} sx={{ my: 2 }}>
77
- <Box className="section">
78
- <SectionHeader title={t('payment.customer.subscriptions.current')} mb={0} />
79
- <Box className="section-body">
80
- <CurrentSubscriptions
81
- id={data.id}
82
- onChange={runAsync}
83
- style={{
84
- cursor: 'pointer',
85
- }}
86
- onClickSubscription={(subscription) => {
87
- navigate(`/customer/subscription/${subscription.id}`);
88
- }}
89
- />
76
+ <>
77
+ <ProgressBar pending={isPending} />
78
+ <Grid container spacing={5}>
79
+ <Grid item xs={12} md={8}>
80
+ <Root direction="column" spacing={3} sx={{ my: 2 }}>
81
+ <Box className="section">
82
+ <SectionHeader title={t('payment.customer.subscriptions.current')} mb={0} />
83
+ <Box className="section-body">
84
+ <CurrentSubscriptions
85
+ id={data.id}
86
+ onChange={runAsync}
87
+ style={{
88
+ cursor: 'pointer',
89
+ }}
90
+ onClickSubscription={(subscription) => {
91
+ startTransition(() => {
92
+ navigate(`/customer/subscription/${subscription.id}`);
93
+ });
94
+ }}
95
+ />
96
+ </Box>
90
97
  </Box>
91
- </Box>
92
- <Box className="section">
93
- <SectionHeader title={t('payment.customer.invoices')} mb={0}>
94
- {isEmpty(data.summary.due) === false && (
95
- <Tooltip title={t('payment.customer.pastDue.warning')}>
96
- <Button
97
- variant="contained"
98
- color="error"
99
- component="a"
100
- size="small"
101
- href={joinURL(window.location.origin, getPrefix(), '/customer/invoice/past-due')}
102
- target="_blank"
103
- rel="noreferrer"
104
- style={{ textDecoration: 'none' }}>
105
- {t('payment.customer.pastDue.invoices')}
106
- </Button>
107
- </Tooltip>
108
- )}
109
- </SectionHeader>
110
- <Box className="section-body">
111
- <CustomerInvoiceList customer_id={data.id} />
98
+ <Box className="section">
99
+ <SectionHeader title={t('payment.customer.invoices')} mb={0}>
100
+ {isEmpty(data.summary.due) === false && (
101
+ <Tooltip title={t('payment.customer.pastDue.warning')}>
102
+ <Button
103
+ variant="contained"
104
+ color="error"
105
+ component="a"
106
+ size="small"
107
+ href={joinURL(window.location.origin, getPrefix(), '/customer/invoice/past-due')}
108
+ target="_blank"
109
+ rel="noreferrer"
110
+ style={{ textDecoration: 'none' }}>
111
+ {t('payment.customer.pastDue.invoices')}
112
+ </Button>
113
+ </Tooltip>
114
+ )}
115
+ </SectionHeader>
116
+ <Box className="section-body">
117
+ <CustomerInvoiceList customer_id={data.id} />
118
+ </Box>
112
119
  </Box>
113
- </Box>
114
- </Root>
115
- </Grid>
116
- <Grid item xs={12} md={4}>
117
- <Root direction="column" spacing={4} sx={{ my: 2 }}>
118
- <Box className="section">
119
- <SectionHeader title={t('payment.customer.details')}>
120
- <Button
121
- variant="outlined"
122
- color="inherit"
123
- size="small"
124
- disabled={state.editing || state.loading}
125
- onClick={() => setState({ editing: true })}>
126
- <Edit fontSize="small" sx={{ mr: 0.5 }} />
127
- {t('payment.customer.update')}
128
- </Button>
129
- </SectionHeader>
130
- <Stack>
131
- <InfoRow sizes={[1, 2]} label={t('common.did')} value={<DID copyable>{data.did}</DID>} />
132
- <InfoRow sizes={[1, 2]} label={t('admin.customer.name')} value={data.name} />
133
- <InfoRow sizes={[1, 2]} label={t('admin.customer.phone')} value={data.phone} />
134
- <InfoRow sizes={[1, 2]} label={t('admin.customer.email')} value={data.email} />
135
- <InfoRow
136
- sizes={[1, 2]}
137
- label={t('admin.customer.address.country')}
138
- value={
139
- data.address?.country ? (
140
- <FlagEmoji iso2={data.address?.country} style={{ display: 'flex', width: 24 }} />
141
- ) : (
142
- ''
143
- )
144
- }
145
- />
146
- <InfoRow sizes={[1, 2]} label={t('admin.customer.address.state')} value={data.address?.state} />
147
- <InfoRow sizes={[1, 2]} label={t('admin.customer.address.city')} value={data.address?.city} />
148
- <InfoRow sizes={[1, 2]} label={t('admin.customer.address.line1')} value={data.address?.line1} />
149
- <InfoRow sizes={[1, 2]} label={t('admin.customer.address.line2')} value={data.address?.line2} />
150
- <InfoRow
151
- sizes={[1, 2]}
152
- label={t('admin.customer.address.postal_code')}
153
- value={data.address?.postal_code}
154
- />
155
- </Stack>
156
- {state.editing && (
157
- <EditCustomer
158
- data={data}
159
- loading={state.loading}
160
- onSave={onUpdateInfo}
161
- onCancel={() => setState({ editing: false })}
162
- />
163
- )}
164
- </Box>
165
- <Box className="section">
166
- <SectionHeader title={t('payment.customer.summary')} />
167
- <PaymentProvider session={session} connect={connectApi}>
168
- <Stack
169
- className="section-body"
170
- direction="column"
171
- spacing={2}
172
- justifyContent="flex-start"
173
- sx={{ width: '100%' }}>
174
- <InfoMetric label={t('admin.customer.spent')} value={<BalanceList data={data.summary.paid} />} />
175
- <InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} />
176
- <InfoMetric label={t('admin.customer.refund')} value={<BalanceList data={data.summary.refunded} />} />
120
+ </Root>
121
+ </Grid>
122
+ <Grid item xs={12} md={4}>
123
+ <Root direction="column" spacing={4} sx={{ my: 2 }}>
124
+ <Box className="section">
125
+ <SectionHeader title={t('payment.customer.details')}>
126
+ <Button
127
+ variant="outlined"
128
+ color="inherit"
129
+ size="small"
130
+ disabled={state.editing || state.loading}
131
+ onClick={() => setState({ editing: true })}>
132
+ <Edit fontSize="small" sx={{ mr: 0.5 }} />
133
+ {t('payment.customer.update')}
134
+ </Button>
135
+ </SectionHeader>
136
+ <Stack>
137
+ <InfoRow sizes={[1, 2]} label={t('common.did')} value={<DID copyable>{data.did}</DID>} />
138
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.name')} value={data.name} />
139
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.phone')} value={data.phone} />
140
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.email')} value={data.email} />
141
+ <InfoRow
142
+ sizes={[1, 2]}
143
+ label={t('admin.customer.address.country')}
144
+ value={
145
+ data.address?.country ? (
146
+ <FlagEmoji iso2={data.address?.country} style={{ display: 'flex', width: 24 }} />
147
+ ) : (
148
+ ''
149
+ )
150
+ }
151
+ />
152
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.address.state')} value={data.address?.state} />
153
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.address.city')} value={data.address?.city} />
154
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.address.line1')} value={data.address?.line1} />
155
+ <InfoRow sizes={[1, 2]} label={t('admin.customer.address.line2')} value={data.address?.line2} />
156
+ <InfoRow
157
+ sizes={[1, 2]}
158
+ label={t('admin.customer.address.postal_code')}
159
+ value={data.address?.postal_code}
160
+ />
177
161
  </Stack>
178
- </PaymentProvider>
179
- </Box>
180
- </Root>
162
+ {state.editing && (
163
+ <EditCustomer
164
+ data={data}
165
+ loading={state.loading}
166
+ onSave={onUpdateInfo}
167
+ onCancel={() => setState({ editing: false })}
168
+ />
169
+ )}
170
+ </Box>
171
+ <Box className="section">
172
+ <SectionHeader title={t('payment.customer.summary')} />
173
+ <PaymentProvider session={session} connect={connectApi}>
174
+ <Stack
175
+ className="section-body"
176
+ direction="column"
177
+ spacing={2}
178
+ justifyContent="flex-start"
179
+ sx={{ width: '100%' }}>
180
+ <InfoMetric label={t('admin.customer.spent')} value={<BalanceList data={data.summary.paid} />} />
181
+ <InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} />
182
+ <InfoMetric label={t('admin.customer.refund')} value={<BalanceList data={data.summary.refunded} />} />
183
+ </Stack>
184
+ </PaymentProvider>
185
+ </Box>
186
+ </Root>
187
+ </Grid>
181
188
  </Grid>
182
- </Grid>
189
+ </>
183
190
  );
184
191
  }
185
192