payment-kit 1.13.243 → 1.13.245

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.
@@ -212,7 +212,14 @@ router.put('/:id/cancel', authPortal, async (req, res) => {
212
212
  return res.status(400).json({ error: 'Subscription already canceled' });
213
213
  }
214
214
 
215
- const { at = 'current_period_end', refund = 'none', time, feedback = 'other', comment = '' } = req.body;
215
+ const {
216
+ at = 'current_period_end',
217
+ refund = 'none',
218
+ time,
219
+ feedback = 'other',
220
+ comment = '',
221
+ reason = 'payment_disputed',
222
+ } = req.body;
216
223
  if (at === 'custom' && dayjs(time).unix() < dayjs().unix()) {
217
224
  return res.status(400).json({ error: 'cancel at must be a future timestamp' });
218
225
  }
@@ -220,9 +227,9 @@ router.put('/:id/cancel', authPortal, async (req, res) => {
220
227
  // update cancel at
221
228
  const updates: Partial<Subscription> = {
222
229
  cancelation_details: {
223
- reason: 'payment_disputed',
224
- feedback: 'other',
225
- comment: `Requested by ${req.user?.role}:${req.user?.did}`,
230
+ comment: comment || `Requested by ${req.user?.role}:${req.user?.did}`,
231
+ reason: reason || 'payment_disputed',
232
+ feedback: feedback || 'other',
226
233
  },
227
234
  };
228
235
  const now = dayjs().unix() + 3;
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.243
17
+ version: 1.13.245
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.243",
3
+ "version": "1.13.245",
4
4
  "scripts": {
5
5
  "dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -51,7 +51,7 @@
51
51
  "@arcblock/ux": "^2.9.77",
52
52
  "@arcblock/validator": "^1.18.116",
53
53
  "@blocklet/logger": "1.16.26",
54
- "@blocklet/payment-react": "1.13.243",
54
+ "@blocklet/payment-react": "1.13.245",
55
55
  "@blocklet/sdk": "1.16.26",
56
56
  "@blocklet/ui-react": "^2.9.77",
57
57
  "@blocklet/uploader": "^0.1.6",
@@ -116,7 +116,7 @@
116
116
  "devDependencies": {
117
117
  "@abtnode/types": "1.16.26",
118
118
  "@arcblock/eslint-config-ts": "^0.3.0",
119
- "@blocklet/payment-types": "1.13.243",
119
+ "@blocklet/payment-types": "1.13.245",
120
120
  "@types/cookie-parser": "^1.4.7",
121
121
  "@types/cors": "^2.8.17",
122
122
  "@types/dotenv-flow": "^3.3.3",
@@ -155,5 +155,5 @@
155
155
  "parser": "typescript"
156
156
  }
157
157
  },
158
- "gitHead": "b699d54327e794d3b63e5c217ba5cddf759187b7"
158
+ "gitHead": "3e56c0c69652d8cc0f2770b649c93fc0e66321eb"
159
159
  }
@@ -5,11 +5,10 @@ import type { TInvoiceExpanded } from '@blocklet/payment-types';
5
5
  import { CircularProgress, Typography } from '@mui/material';
6
6
  import { useLocalStorageState } from 'ahooks';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } 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';
13
12
  import Table from '../table';
14
13
  import InvoiceActions from './action';
15
14
 
@@ -50,10 +49,6 @@ type ListProps = {
50
49
  ignore_zero?: boolean;
51
50
  include_staking?: boolean;
52
51
 
53
- invoiceProps?: {
54
- onClick?: (invoice: TInvoiceExpanded) => void | Promise<void>;
55
- };
56
-
57
52
  mode?: 'admin' | 'customer';
58
53
  };
59
54
 
@@ -79,8 +74,6 @@ InvoiceList.defaultProps = {
79
74
  include_staking: false,
80
75
  ignore_zero: false,
81
76
 
82
- invoiceProps: {},
83
-
84
77
  mode: 'admin',
85
78
  };
86
79
 
@@ -91,13 +84,11 @@ export default function InvoiceList({
91
84
  status,
92
85
  include_staking,
93
86
  ignore_zero,
94
- invoiceProps,
95
87
  mode,
96
88
  }: ListProps) {
97
89
  const listKey = getListKey({ customer_id, subscription_id });
98
90
 
99
91
  const { t } = useLocaleContext();
100
- const navigate = useNavigate();
101
92
  const [search, setSearch] = useLocalStorageState<SearchProps & { ignore_zero?: boolean; include_staking?: boolean }>(
102
93
  listKey,
103
94
  {
@@ -112,7 +103,6 @@ export default function InvoiceList({
112
103
  },
113
104
  }
114
105
  );
115
- const { startTransition } = useTransitionContext();
116
106
 
117
107
  const [data, setData] = useState({}) as any;
118
108
 
@@ -136,12 +126,14 @@ export default function InvoiceList({
136
126
  align: 'right',
137
127
  options: {
138
128
  customBodyRenderLite: (_: string, index: number) => {
139
- const item = data.list[index];
129
+ const item = data.list[index] as TInvoiceExpanded;
140
130
  return (
141
- <Typography component="strong" fontWeight={600}>
142
- {formatBNStr(item?.total, item?.paymentCurrency.decimal)}&nbsp;
143
- {item?.paymentCurrency.symbol}
144
- </Typography>
131
+ <Link to={`/admin/billing/${item.id}`}>
132
+ <Typography component="strong" fontWeight={600}>
133
+ {formatBNStr(item?.total, item?.paymentCurrency.decimal)}&nbsp;
134
+ {item?.paymentCurrency.symbol}
135
+ </Typography>
136
+ </Link>
145
137
  );
146
138
  },
147
139
  },
@@ -153,24 +145,38 @@ export default function InvoiceList({
153
145
  options: {
154
146
  customBodyRenderLite: (_: string, index: number) => {
155
147
  const item = data.list[index] as TInvoiceExpanded;
156
- return <Status label={item?.status} color={getInvoiceStatusColor(item?.status)} />;
148
+ return (
149
+ <Link to={`/admin/billing/${item.id}`}>
150
+ <Status label={item?.status} color={getInvoiceStatusColor(item?.status)} />
151
+ </Link>
152
+ );
157
153
  },
158
154
  },
159
155
  },
160
156
  {
161
157
  label: t('admin.invoice.number'),
162
158
  name: 'number',
159
+ options: {
160
+ customBodyRenderLite: (_: string, index: number) => {
161
+ const item = data.list[index] as TInvoiceExpanded;
162
+ return <Link to={`/admin/billing/${item.id}`}>{item.number}</Link>;
163
+ },
164
+ },
163
165
  },
164
166
  {
165
167
  label: t('common.description'),
166
168
  name: 'description',
167
169
  options: {
168
170
  customBodyRenderLite: (_: string, index: number) => {
169
- const item = data.list[index];
171
+ const item = data.list[index] as TInvoiceExpanded;
170
172
  const desc = item?.description || item?.id;
171
- return desc.startsWith('Subscription ')
172
- ? t(`payment.invoice.reason.${desc.replace('Subscription ', '')}`)
173
- : desc;
173
+ return (
174
+ <Link to={`/admin/billing/${item.id}`}>
175
+ {desc.startsWith('Subscription ')
176
+ ? t(`payment.invoice.reason.${desc.replace('Subscription ', '')}`)
177
+ : desc}
178
+ </Link>
179
+ );
174
180
  },
175
181
  },
176
182
  },
@@ -179,9 +185,9 @@ export default function InvoiceList({
179
185
  name: 'created_at',
180
186
  options: {
181
187
  sort: true,
182
- responsive: 'vertical',
183
- customBodyRender: (e: string) => {
184
- return formatTime(e);
188
+ customBodyRenderLite: (_: string, index: number) => {
189
+ const item = data.list[index] as TInvoiceExpanded;
190
+ return <Link to={`/admin/billing/${item.id}`}>{formatTime(item.created_at)}</Link>;
185
191
  },
186
192
  },
187
193
  },
@@ -190,8 +196,9 @@ export default function InvoiceList({
190
196
  name: 'updated_at',
191
197
  options: {
192
198
  sort: true,
193
- customBodyRender: (e: string) => {
194
- return formatTime(e);
199
+ customBodyRenderLite: (_: string, index: number) => {
200
+ const item = data.list[index] as TInvoiceExpanded;
201
+ return <Link to={`/admin/billing/${item.id}`}>{formatTime(item.updated_at)}</Link>;
195
202
  },
196
203
  },
197
204
  },
@@ -236,6 +243,7 @@ export default function InvoiceList({
236
243
 
237
244
  return (
238
245
  <Table
246
+ hasRowLink
239
247
  data={data.list}
240
248
  durable={`__${listKey}__`}
241
249
  durableKeys={['searchText']}
@@ -246,15 +254,6 @@ export default function InvoiceList({
246
254
  count: data.count,
247
255
  page: search!.page - 1,
248
256
  rowsPerPage: search!.pageSize,
249
- onRowClick: async (_: any, { dataIndex }: any) => {
250
- const item = data.list[dataIndex] as TInvoiceExpanded;
251
-
252
- if (invoiceProps?.onClick) {
253
- await invoiceProps.onClick(item);
254
- } else {
255
- startTransition(() => navigate(`/admin/billing/${item.id}`));
256
- }
257
- },
258
257
  onColumnSortChange(_: any, order: any) {
259
258
  setSearch({
260
259
  ...search!,
@@ -5,12 +5,11 @@ import type { TPaymentIntentExpanded } from '@blocklet/payment-types';
5
5
  import { CircularProgress, Typography } from '@mui/material';
6
6
  import { useLocalStorageState } from 'ahooks';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } from 'react-router-dom';
9
9
 
10
10
  import { debounce } from '../../libs/util';
11
11
  import CustomerLink from '../customer/link';
12
12
  import FilterToolbar from '../filter-toolbar';
13
- import { useTransitionContext } from '../progress-bar';
14
13
  import Table from '../table';
15
14
  import PaymentIntentActions from './actions';
16
15
 
@@ -71,7 +70,6 @@ PaymentList.defaultProps = {
71
70
 
72
71
  export default function PaymentList({ customer_id, invoice_id, features }: ListProps) {
73
72
  const { t } = useLocaleContext();
74
- const navigate = useNavigate();
75
73
 
76
74
  const listKey = getListKey({ customer_id, invoice_id });
77
75
  const [search, setSearch] = useLocalStorageState<SearchProps>(listKey, {
@@ -84,7 +82,6 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
84
82
  },
85
83
  });
86
84
 
87
- const { startTransition } = useTransitionContext();
88
85
  const [data, setData] = useState({}) as any;
89
86
 
90
87
  useEffect(() => {
@@ -109,17 +106,19 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
109
106
  customBodyRenderLite: (_: string, index: number) => {
110
107
  const item = data.list[index] as TPaymentIntentExpanded;
111
108
  return (
112
- <Typography
113
- component="strong"
114
- sx={{ color: item.amount_received === '0' ? 'warning.main' : 'inherit' }}
115
- fontWeight={600}>
116
- {formatBNStr(
117
- item.amount_received === '0' ? item.amount : item.amount_received,
118
- item?.paymentCurrency.decimal
119
- )}
120
- &nbsp;
121
- {item?.paymentCurrency.symbol}
122
- </Typography>
109
+ <Link to={`/admin/payments/${item.id}`}>
110
+ <Typography
111
+ component="strong"
112
+ sx={{ color: item.amount_received === '0' ? 'warning.main' : 'inherit' }}
113
+ fontWeight={600}>
114
+ {formatBNStr(
115
+ item.amount_received === '0' ? item.amount : item.amount_received,
116
+ item?.paymentCurrency.decimal
117
+ )}
118
+ &nbsp;
119
+ {item?.paymentCurrency.symbol}
120
+ </Typography>
121
+ </Link>
123
122
  );
124
123
  },
125
124
  },
@@ -131,7 +130,11 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
131
130
  options: {
132
131
  customBodyRenderLite: (_: string, index: number) => {
133
132
  const item = data.list[index] as TPaymentIntentExpanded;
134
- return <Status label={item.status} color={getPaymentIntentStatusColor(item.status)} />;
133
+ return (
134
+ <Link to={`/admin/payments/${item.id}`}>
135
+ <Status label={item.status} color={getPaymentIntentStatusColor(item.status)} />
136
+ </Link>
137
+ );
135
138
  },
136
139
  },
137
140
  },
@@ -141,7 +144,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
141
144
  options: {
142
145
  customBodyRenderLite: (_: string, index: number) => {
143
146
  const item = data.list[index] as TPaymentIntentExpanded;
144
- return item.description || item.id;
147
+ return <Link to={`/admin/payments/${item.id}`}>{item.description || item.id}</Link>;
145
148
  },
146
149
  },
147
150
  },
@@ -150,8 +153,9 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
150
153
  name: 'created_at',
151
154
  options: {
152
155
  sort: true,
153
- customBodyRender: (e: string) => {
154
- return formatTime(e);
156
+ customBodyRenderLite: (_: string, index: number) => {
157
+ const item = data.list[index] as TPaymentIntentExpanded;
158
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.created_at)}</Link>;
155
159
  },
156
160
  },
157
161
  },
@@ -160,8 +164,9 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
160
164
  name: 'updated_at',
161
165
  options: {
162
166
  sort: true,
163
- customBodyRender: (e: string) => {
164
- return formatTime(e);
167
+ customBodyRenderLite: (_: string, index: number) => {
168
+ const item = data.list[index] as TPaymentIntentExpanded;
169
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.updated_at)}</Link>;
165
170
  },
166
171
  },
167
172
  },
@@ -202,6 +207,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
202
207
 
203
208
  return (
204
209
  <Table
210
+ hasRowLink
205
211
  data={data.list || []}
206
212
  durable={`__${listKey}__`}
207
213
  durableKeys={['searchText']}
@@ -239,12 +245,6 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
239
245
  o: order,
240
246
  });
241
247
  },
242
- onRowClick: (_: any, { dataIndex }: any) => {
243
- const item = data.list[dataIndex] as TPaymentIntentExpanded;
244
- startTransition(() => {
245
- navigate(`/admin/payments/${item.id}`);
246
- });
247
- },
248
248
  }}
249
249
  toolbar={features?.toolbar}
250
250
  footer={features?.footer}
@@ -5,12 +5,11 @@ import type { TPayoutExpanded } from '@blocklet/payment-types';
5
5
  import { CircularProgress, Typography } from '@mui/material';
6
6
  import { useLocalStorageState } from 'ahooks';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } from 'react-router-dom';
9
9
 
10
10
  import { debounce } from '../../libs/util';
11
11
  import CustomerLink from '../customer/link';
12
12
  import FilterToolbar from '../filter-toolbar';
13
- import { useTransitionContext } from '../progress-bar';
14
13
  import Table from '../table';
15
14
  import PayoutActions from './actions';
16
15
 
@@ -73,7 +72,6 @@ PayoutList.defaultProps = {
73
72
 
74
73
  export default function PayoutList({ customer_id, payment_intent_id, status, features }: ListProps) {
75
74
  const { t } = useLocaleContext();
76
- const navigate = useNavigate();
77
75
 
78
76
  const listKey = getListKey({ customer_id, payment_intent_id });
79
77
  const [search, setSearch] = useLocalStorageState<SearchProps>(listKey, {
@@ -86,7 +84,6 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
86
84
  },
87
85
  });
88
86
 
89
- const { startTransition } = useTransitionContext();
90
87
  const [data, setData] = useState({}) as any;
91
88
 
92
89
  useEffect(() => {
@@ -111,11 +108,13 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
111
108
  customBodyRenderLite: (_: string, index: number) => {
112
109
  const item = data.list[index] as TPayoutExpanded;
113
110
  return (
114
- <Typography component="strong" fontWeight={600}>
115
- {formatBNStr(item.amount, item?.paymentCurrency.decimal)}
116
- &nbsp;
117
- {item?.paymentCurrency.symbol}
118
- </Typography>
111
+ <Link to={`/admin/payments/${item.id}`}>
112
+ <Typography component="strong" fontWeight={600}>
113
+ {formatBNStr(item.amount, item?.paymentCurrency.decimal)}
114
+ &nbsp;
115
+ {item?.paymentCurrency.symbol}
116
+ </Typography>
117
+ </Link>
119
118
  );
120
119
  },
121
120
  },
@@ -127,7 +126,11 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
127
126
  options: {
128
127
  customBodyRenderLite: (_: string, index: number) => {
129
128
  const item = data.list[index] as TPayoutExpanded;
130
- return <Status label={item.status} color={getPayoutStatusColor(item.status)} />;
129
+ return (
130
+ <Link to={`/admin/payments/${item.id}`}>
131
+ <Status label={item.status} color={getPayoutStatusColor(item.status)} />
132
+ </Link>
133
+ );
131
134
  },
132
135
  },
133
136
  },
@@ -137,7 +140,7 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
137
140
  options: {
138
141
  customBodyRenderLite: (_: string, index: number) => {
139
142
  const item = data.list[index] as TPayoutExpanded;
140
- return item.description || item.id;
143
+ return <Link to={`/admin/payments/${item.id}`}>{item.description || item.id}</Link>;
141
144
  },
142
145
  },
143
146
  },
@@ -146,8 +149,9 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
146
149
  name: 'created_at',
147
150
  options: {
148
151
  sort: true,
149
- customBodyRender: (e: string) => {
150
- return formatTime(e);
152
+ customBodyRenderLite: (_: string, index: number) => {
153
+ const item = data.list[index] as TPayoutExpanded;
154
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.updated_at)}</Link>;
151
155
  },
152
156
  },
153
157
  },
@@ -156,8 +160,9 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
156
160
  name: 'updated_at',
157
161
  options: {
158
162
  sort: true,
159
- customBodyRender: (e: string) => {
160
- return formatTime(e);
163
+ customBodyRenderLite: (_: string, index: number) => {
164
+ const item = data.list[index] as TPayoutExpanded;
165
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.updated_at)}</Link>;
161
166
  },
162
167
  },
163
168
  },
@@ -198,6 +203,8 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
198
203
 
199
204
  return (
200
205
  <Table
206
+ hasRowLink
207
+ durable={`__${listKey}__`}
201
208
  data={data.list || []}
202
209
  columns={columns}
203
210
  loading={!data.list}
@@ -233,12 +240,6 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
233
240
  o: order,
234
241
  });
235
242
  },
236
- onRowClick: (_: any, { dataIndex }: any) => {
237
- const item = data.list[dataIndex] as TPayoutExpanded;
238
- startTransition(() => {
239
- navigate(`/admin/payments/${item.id}`);
240
- });
241
- },
242
243
  }}
243
244
  toolbar={features?.toolbar}
244
245
  footer={features?.footer}
@@ -5,11 +5,10 @@ import type { TRefundExpanded } from '@blocklet/payment-types';
5
5
  import { CircularProgress, Typography } from '@mui/material';
6
6
  import { useLocalStorageState } from 'ahooks';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } 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';
13
12
  import Table from '../table';
14
13
  import RefundActions from './actions';
15
14
 
@@ -78,8 +77,6 @@ RefundList.defaultProps = {
78
77
 
79
78
  export default function RefundList({ customer_id, invoice_id, subscription_id, status, features }: ListProps) {
80
79
  const { t } = useLocaleContext();
81
- const navigate = useNavigate();
82
- const { startTransition } = useTransitionContext();
83
80
  const listKey = getListKey({ customer_id, invoice_id, subscription_id });
84
81
 
85
82
  const [search, setSearch] = useLocalStorageState<SearchProps>(listKey, {
@@ -118,10 +115,12 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
118
115
  customBodyRenderLite: (_: string, index: number) => {
119
116
  const item = data.list[index] as TRefundExpanded;
120
117
  return (
121
- <Typography component="strong" fontWeight={600}>
122
- {formatBNStr(item?.amount, item?.paymentCurrency.decimal)}&nbsp;
123
- {item?.paymentCurrency.symbol}
124
- </Typography>
118
+ <Link to={`/admin/payments/${item.id}`}>
119
+ <Typography component="strong" fontWeight={600}>
120
+ {formatBNStr(item?.amount, item?.paymentCurrency.decimal)}&nbsp;
121
+ {item?.paymentCurrency.symbol}
122
+ </Typography>
123
+ </Link>
125
124
  );
126
125
  },
127
126
  },
@@ -134,7 +133,11 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
134
133
  filter: true,
135
134
  customBodyRenderLite: (_: string, index: number) => {
136
135
  const item = data.list[index] as TRefundExpanded;
137
- return <Status label={item.status} color={getPaymentIntentStatusColor(item.status)} />;
136
+ return (
137
+ <Link to={`/admin/payments/${item.id}`}>
138
+ <Status label={item.status} color={getPaymentIntentStatusColor(item.status)} />
139
+ </Link>
140
+ );
138
141
  },
139
142
  },
140
143
  },
@@ -144,7 +147,7 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
144
147
  options: {
145
148
  customBodyRenderLite: (_: string, index: number) => {
146
149
  const item = data.list[index] as TRefundExpanded;
147
- return item.description || item.id;
150
+ return <Link to={`/admin/payments/${item.id}`}>{item.description || item.id}</Link>;
148
151
  },
149
152
  },
150
153
  },
@@ -153,8 +156,20 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
153
156
  name: 'created_at',
154
157
  options: {
155
158
  sort: true,
156
- customBodyRender: (e: string) => {
157
- return formatTime(e);
159
+ customBodyRenderLite: (_: string, index: number) => {
160
+ const item = data.list[index] as TRefundExpanded;
161
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.created_at)}</Link>;
162
+ },
163
+ },
164
+ },
165
+ {
166
+ label: t('common.updatedAt'),
167
+ name: 'updated_at',
168
+ options: {
169
+ sort: true,
170
+ customBodyRenderLite: (_: string, index: number) => {
171
+ const item = data.list[index] as TRefundExpanded;
172
+ return <Link to={`/admin/payments/${item.id}`}>{formatTime(item.updated_at)}</Link>;
158
173
  },
159
174
  },
160
175
  },
@@ -194,6 +209,7 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
194
209
 
195
210
  return (
196
211
  <Table
212
+ hasRowLink
197
213
  data={data.list}
198
214
  durable={`__${listKey}__`}
199
215
  durableKeys={['searchText']}
@@ -204,12 +220,6 @@ export default function RefundList({ customer_id, invoice_id, subscription_id, s
204
220
  count: data.count,
205
221
  page: search!.page - 1,
206
222
  rowsPerPage: search!.pageSize,
207
- onRowClick: (_: any, { dataIndex }: any) => {
208
- const item = data.list[dataIndex] as TRefundExpanded;
209
- startTransition(() => {
210
- navigate(`/admin/payments/${item.id}`);
211
- });
212
- },
213
223
  onColumnSortChange(_: any, order: any) {
214
224
  setSearch({
215
225
  ...search!,
@@ -5,11 +5,10 @@ import type { TSubscriptionExpanded } from '@blocklet/payment-types';
5
5
  import { CircularProgress } from '@mui/material';
6
6
  import { useLocalStorageState } from 'ahooks';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } 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';
13
12
  import Table from '../table';
14
13
  import SubscriptionActions from './actions';
15
14
  import SubscriptionDescription from './description';
@@ -70,8 +69,6 @@ export default function SubscriptionList({ customer_id, features, status }: List
70
69
  const listKey = getListKey({ customer_id });
71
70
 
72
71
  const { t } = useLocaleContext();
73
- const navigate = useNavigate();
74
- const { startTransition } = useTransitionContext();
75
72
  const [search, setSearch] = useLocalStorageState<SearchProps>(listKey, {
76
73
  defaultValue: {
77
74
  status: (status || 'active') as string,
@@ -103,7 +100,11 @@ export default function SubscriptionList({ customer_id, features, status }: List
103
100
  options: {
104
101
  customBodyRenderLite: (_: string, index: number) => {
105
102
  const item = data.list[index] as TSubscriptionExpanded;
106
- return <SubscriptionDescription subscription={item} />;
103
+ return (
104
+ <Link to={`/admin/billing/${item.id}`}>
105
+ <SubscriptionDescription subscription={item} />
106
+ </Link>
107
+ );
107
108
  },
108
109
  },
109
110
  },
@@ -114,7 +115,11 @@ export default function SubscriptionList({ customer_id, features, status }: List
114
115
  filter: true,
115
116
  customBodyRenderLite: (_: string, index: number) => {
116
117
  const item = data.list[index] as TSubscriptionExpanded;
117
- return <SubscriptionStatus subscription={item} />;
118
+ return (
119
+ <Link to={`/admin/billing/${item.id}`}>
120
+ <SubscriptionStatus subscription={item} />
121
+ </Link>
122
+ );
118
123
  },
119
124
  },
120
125
  },
@@ -125,10 +130,12 @@ export default function SubscriptionList({ customer_id, features, status }: List
125
130
  customBodyRenderLite: (_: string, index: number) => {
126
131
  const item = data.list[index] as TSubscriptionExpanded;
127
132
  return (
128
- <Status
129
- label={item.collection_method === 'charge_automatically' ? 'Auto' : 'Manual'}
130
- color={item.collection_method === 'charge_automatically' ? 'default' : 'warning'}
131
- />
133
+ <Link to={`/admin/billing/${item.id}`}>
134
+ <Status
135
+ label={item.collection_method === 'charge_automatically' ? 'Auto' : 'Manual'}
136
+ color={item.collection_method === 'charge_automatically' ? 'default' : 'warning'}
137
+ />
138
+ </Link>
132
139
  );
133
140
  },
134
141
  },
@@ -138,8 +145,9 @@ export default function SubscriptionList({ customer_id, features, status }: List
138
145
  name: 'created_at',
139
146
  options: {
140
147
  sort: true,
141
- customBodyRender: (e: string) => {
142
- return formatTime(e);
148
+ customBodyRenderLite: (_: string, index: number) => {
149
+ const item = data.list[index] as TSubscriptionExpanded;
150
+ return <Link to={`/admin/billing/${item.id}`}>{formatTime(item.created_at)}</Link>;
143
151
  },
144
152
  },
145
153
  },
@@ -184,6 +192,7 @@ export default function SubscriptionList({ customer_id, features, status }: List
184
192
 
185
193
  return (
186
194
  <Table
195
+ hasRowLink
187
196
  data={data.list}
188
197
  columns={columns}
189
198
  loading={!data.list}
@@ -194,12 +203,6 @@ export default function SubscriptionList({ customer_id, features, status }: List
194
203
  count: data.count,
195
204
  page: search!.page - 1,
196
205
  rowsPerPage: search!.pageSize,
197
- onRowClick: (_: any, { dataIndex }: any) => {
198
- const item = data.list[dataIndex] as TSubscriptionExpanded;
199
- startTransition(() => {
200
- navigate(`/admin/billing/${item.id}`);
201
- });
202
- },
203
206
  onColumnSortChange(_: any, order: any) {
204
207
  setSearch({
205
208
  ...search!,
@@ -8,7 +8,7 @@ function EmptyStub() {
8
8
  return null;
9
9
  }
10
10
 
11
- export default function Table({ options, columns, toolbar = true, footer = true, ...rest }: any) {
11
+ export default function Table({ options, columns, toolbar = true, footer = true, hasRowLink = false, ...rest }: any) {
12
12
  const { isMobile } = useMobile();
13
13
  const { locale } = useLocaleContext();
14
14
 
@@ -46,12 +46,25 @@ export default function Table({ options, columns, toolbar = true, footer = true,
46
46
  })}
47
47
  {...rest}
48
48
  components={components}
49
+ hasRowLink={hasRowLink}
49
50
  isMobile={isMobile}
50
51
  />
51
52
  );
52
53
  }
53
54
 
54
55
  const Wrapped = styled(Datatable)`
56
+ ${(props) =>
57
+ props.hasRowLink
58
+ ? `.MuiTableCell-root {
59
+ font-size: 1rem !important;
60
+ padding: 0;
61
+ }`
62
+ : ''}
63
+
64
+ th.MuiTableCell-head {
65
+ padding: 8px 24px 8px 0;
66
+ }
67
+
55
68
  tr.MuiTableRow-root:not(.MuiTableRow-footer) {
56
69
  border-bottom: 1px solid rgba(224, 224, 224, 0.4);
57
70
  }
@@ -64,17 +77,17 @@ const Wrapped = styled(Datatable)`
64
77
  background: #f5f5f5;
65
78
  }
66
79
 
80
+ th a,
81
+ td a {
82
+ text-decoration: none;
83
+ display: block;
84
+ color: inherit;
85
+ padding-top: 8px;
86
+ padding-bottom: 8px;
87
+ padding-right: 24px;
88
+ }
89
+
67
90
  > div {
68
91
  overflow: visible;
69
92
  }
70
-
71
- ${(props) => {
72
- if (props.isMobile) {
73
- return '';
74
- }
75
-
76
- return `.MuiTableRow-root > td:first-of-type{
77
- padding-left: 6px;
78
- }`;
79
- }}
80
93
  `;
package/src/global.css CHANGED
@@ -63,10 +63,6 @@ th.MuiTableCell-head {
63
63
  padding: 8px 24px 8px 0;
64
64
  }
65
65
 
66
- .MuiTableRow-hover {
67
- cursor: pointer;
68
- }
69
-
70
66
  .MuiRadio-root {
71
67
  padding: 4px;
72
68
  }
@@ -6,9 +6,8 @@ import { api, formatTime } from '@blocklet/payment-react';
6
6
  import type { TCustomer } from '@blocklet/payment-types';
7
7
  import { Avatar, CircularProgress, Stack, Typography } from '@mui/material';
8
8
  import { useEffect, useState } from 'react';
9
- import { useNavigate } from 'react-router-dom';
9
+ import { Link } from 'react-router-dom';
10
10
 
11
- import { useTransitionContext } from '../../../../components/progress-bar';
12
11
  import Table from '../../../../components/table';
13
12
 
14
13
  const fetchData = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
@@ -30,8 +29,6 @@ export default function CustomersList() {
30
29
  const persisted = getDurableData(listKey);
31
30
 
32
31
  const { t } = useLocaleContext();
33
- const navigate = useNavigate();
34
- const { startTransition } = useTransitionContext();
35
32
  const [search, setSearch] = useState<{ active?: string; pageSize: number; page: number; q?: any; o?: string }>({
36
33
  active: '',
37
34
  o: 'desc',
@@ -62,10 +59,12 @@ export default function CustomersList() {
62
59
  customBodyRenderLite: (_: string, index: number) => {
63
60
  const item = data.list[index] as TCustomer;
64
61
  return (
65
- <Stack direction="row" alignItems="center" spacing={0.5}>
66
- <Avatar src={`/.well-known/service/user/avatar/${item.did}?imageFilter=resize&w=48&h=48`} />
67
- <Typography>{item.name}</Typography>
68
- </Stack>
62
+ <Link to={`/admin/customers/${item.id}`}>
63
+ <Stack direction="row" alignItems="center" spacing={0.5}>
64
+ <Avatar src={`/.well-known/service/user/avatar/${item.did}?imageFilter=resize&w=48&h=48`} />
65
+ <Typography>{item.name}</Typography>
66
+ </Stack>
67
+ </Link>
69
68
  );
70
69
  },
71
70
  },
@@ -76,25 +75,42 @@ export default function CustomersList() {
76
75
  options: {
77
76
  customBodyRenderLite: (_: string, index: number) => {
78
77
  const item = data.list[index] as TCustomer;
79
- return <DidAddress did={item.did} />;
78
+ return (
79
+ <Link to={`/admin/customers/${item.id}`}>
80
+ <DidAddress did={item.did} />
81
+ </Link>
82
+ );
80
83
  },
81
84
  },
82
85
  },
83
86
  {
84
87
  label: t('common.email'),
85
88
  name: 'email',
89
+ options: {
90
+ customBodyRenderLite: (_: string, index: number) => {
91
+ const item = data.list[index] as TCustomer;
92
+ return <Link to={`/admin/customers/${item.id}`}>{item.email}</Link>;
93
+ },
94
+ },
86
95
  },
87
96
  {
88
97
  label: t('admin.customer.phone'),
89
98
  name: 'phone',
99
+ options: {
100
+ customBodyRenderLite: (_: string, index: number) => {
101
+ const item = data.list[index] as TCustomer;
102
+ return <Link to={`/admin/customers/${item.id}`}>{item.phone}</Link>;
103
+ },
104
+ },
90
105
  },
91
106
  {
92
107
  label: t('common.createdAt'),
93
108
  name: 'created_at',
94
109
  options: {
95
110
  sort: true,
96
- customBodyRender: (e: string) => {
97
- return formatTime(e);
111
+ customBodyRenderLite: (_: string, index: number) => {
112
+ const item = data.list[index] as TCustomer;
113
+ return <Link to={`/admin/customers/${item.id}`}>{formatTime(item.updated_at)}</Link>;
98
114
  },
99
115
  },
100
116
  },
@@ -110,6 +126,7 @@ export default function CustomersList() {
110
126
 
111
127
  return (
112
128
  <Table
129
+ hasRowLink
113
130
  durable={`__${listKey}__`}
114
131
  durableKeys={['page', 'rowsPerPage', 'searchText']}
115
132
  data={data.list}
@@ -118,12 +135,6 @@ export default function CustomersList() {
118
135
  count: data.count,
119
136
  page: search.page - 1,
120
137
  rowsPerPage: search.pageSize,
121
- onRowClick: (_: any, { dataIndex }: any) => {
122
- const item = data.list[dataIndex] as TCustomer;
123
- startTransition(() => {
124
- navigate(`/admin/customers/${item.id}`);
125
- });
126
- },
127
138
  onColumnSortChange(_: any, order: any) {
128
139
  setSearch({
129
140
  ...search,
@@ -6,13 +6,12 @@ import type { TPaymentLinkExpanded } from '@blocklet/payment-types';
6
6
  import { Alert, CircularProgress, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
7
7
  import { useRequest } from 'ahooks';
8
8
  import { useEffect, useState } from 'react';
9
- import { useNavigate } from 'react-router-dom';
9
+ import { Link } from 'react-router-dom';
10
10
  import { joinURL } from 'ufo';
11
11
  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';
16
15
  import Table from '../../../../components/table';
17
16
  import { ProductsProvider } from '../../../../contexts/products';
18
17
  import { formatPaymentLinkPricing } from '../../../../libs/util';
@@ -29,8 +28,6 @@ function PaymentLinks() {
29
28
  const listKey = 'payment-links';
30
29
  const { t } = useLocaleContext();
31
30
  const { settings } = usePaymentContext();
32
- const navigate = useNavigate();
33
- const { startTransition } = useTransitionContext();
34
31
 
35
32
  const persisted = getDurableData(listKey);
36
33
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
@@ -74,14 +71,24 @@ function PaymentLinks() {
74
71
  name: 'active',
75
72
  options: {
76
73
  customBodyRenderLite: (_: string, index: number) => {
77
- const item = data.list[index];
78
- return <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />;
74
+ const item = data.list[index] as TPaymentLinkExpanded;
75
+ return (
76
+ <Link to={`/admin/products/${item.id}`}>
77
+ <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />
78
+ </Link>
79
+ );
79
80
  },
80
81
  },
81
82
  },
82
83
  {
83
84
  label: t('common.title'),
84
85
  name: 'name',
86
+ options: {
87
+ customBodyRenderLite: (_: string, index: number) => {
88
+ const item = data.list[index] as TPaymentLinkExpanded;
89
+ return <Link to={`/admin/products/${item.id}`}>{item.name}</Link>;
90
+ },
91
+ },
85
92
  },
86
93
  {
87
94
  label: t('admin.price.name'),
@@ -89,9 +96,11 @@ function PaymentLinks() {
89
96
  options: {
90
97
  sort: false,
91
98
  customBodyRenderLite: (_: string, index: number) => {
92
- const doc = data.list[index] as TPaymentLinkExpanded;
93
- const result = formatPaymentLinkPricing(doc, settings.baseCurrency);
94
- return [result.amount, result.then].filter(Boolean).join(', ');
99
+ const item = data.list[index] as TPaymentLinkExpanded;
100
+ const result = formatPaymentLinkPricing(item, settings.baseCurrency);
101
+ return (
102
+ <Link to={`/admin/products/${item.id}`}>{[result.amount, result.then].filter(Boolean).join(', ')}</Link>
103
+ );
95
104
  },
96
105
  },
97
106
  },
@@ -100,8 +109,9 @@ function PaymentLinks() {
100
109
  name: 'created_at',
101
110
  options: {
102
111
  sort: true,
103
- customBodyRender: (e: string) => {
104
- return formatTime(e);
112
+ customBodyRenderLite: (_: string, index: number) => {
113
+ const item = data.list[index] as TPaymentLinkExpanded;
114
+ return <Link to={`/admin/products/${item.id}`}>{formatTime(item.created_at)}</Link>;
105
115
  },
106
116
  },
107
117
  },
@@ -111,8 +121,8 @@ function PaymentLinks() {
111
121
  options: {
112
122
  sort: false,
113
123
  customBodyRenderLite: (_: string, index: number) => {
114
- const doc = data.list[index] as TPaymentLinkExpanded;
115
- return <PaymentLinkActions data={doc} onChange={refresh} />;
124
+ const item = data.list[index] as TPaymentLinkExpanded;
125
+ return <PaymentLinkActions data={item} onChange={refresh} />;
116
126
  },
117
127
  },
118
128
  },
@@ -128,6 +138,7 @@ function PaymentLinks() {
128
138
 
129
139
  return (
130
140
  <Table
141
+ hasRowLink
131
142
  durable={`__${listKey}__`}
132
143
  durableKeys={['page', 'rowsPerPage', 'searchText']}
133
144
  title={
@@ -148,12 +159,6 @@ function PaymentLinks() {
148
159
  count: data.count,
149
160
  page: search.page - 1,
150
161
  rowsPerPage: search.pageSize,
151
- onRowClick: (_: any, { dataIndex }: any) => {
152
- const item = data.list[dataIndex] as TPaymentLinkExpanded;
153
- startTransition(() => {
154
- navigate(`/admin/products/${item.id}`);
155
- });
156
- },
157
162
  }}
158
163
  loading={loading}
159
164
  onChange={onTableChange}
@@ -6,13 +6,12 @@ import type { TPricingTableExpanded } from '@blocklet/payment-types';
6
6
  import { Alert, CircularProgress, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
7
7
  import { useRequest } from 'ahooks';
8
8
  import { useEffect, useState } from 'react';
9
- import { useNavigate } from 'react-router-dom';
9
+ import { Link } from 'react-router-dom';
10
10
  import { joinURL } from 'ufo';
11
11
  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';
16
15
  import Table from '../../../../components/table';
17
16
  import { ProductsProvider } from '../../../../contexts/products';
18
17
 
@@ -27,8 +26,6 @@ const fetchData = (params: Record<string, any> = {}): Promise<{ list: TPricingTa
27
26
  function PricingTables() {
28
27
  const listKey = 'pricing-tables';
29
28
  const { t } = useLocaleContext();
30
- const navigate = useNavigate();
31
- const { startTransition } = useTransitionContext();
32
29
 
33
30
  const persisted = getDurableData(listKey);
34
31
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
@@ -73,22 +70,33 @@ function PricingTables() {
73
70
  name: 'active',
74
71
  options: {
75
72
  customBodyRenderLite: (_: string, index: number) => {
76
- const item = data.list[index];
77
- return <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />;
73
+ const item = data.list[index] as TPricingTableExpanded;
74
+ return (
75
+ <Link to={`/admin/products/${item.id}`}>
76
+ <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />
77
+ </Link>
78
+ );
78
79
  },
79
80
  },
80
81
  },
81
82
  {
82
83
  label: t('common.title'),
83
84
  name: 'name',
85
+ options: {
86
+ customBodyRenderLite: (_: string, index: number) => {
87
+ const item = data.list[index] as TPricingTableExpanded;
88
+ return <Link to={`/admin/products/${item.id}`}>{item.name}</Link>;
89
+ },
90
+ },
84
91
  },
85
92
  {
86
93
  label: t('common.createdAt'),
87
94
  name: 'created_at',
88
95
  options: {
89
96
  sort: true,
90
- customBodyRender: (e: string) => {
91
- return formatTime(e);
97
+ customBodyRenderLite: (_: string, index: number) => {
98
+ const item = data.list[index] as TPricingTableExpanded;
99
+ return <Link to={`/admin/products/${item.id}`}>{formatTime(item.created_at)}</Link>;
92
100
  },
93
101
  },
94
102
  },
@@ -115,6 +123,7 @@ function PricingTables() {
115
123
 
116
124
  return (
117
125
  <Table
126
+ hasRowLink
118
127
  durable={`__${listKey}__`}
119
128
  durableKeys={['page', 'rowsPerPage', 'searchText']}
120
129
  title={
@@ -135,12 +144,6 @@ function PricingTables() {
135
144
  count: data.count,
136
145
  page: search.page - 1,
137
146
  rowsPerPage: search.pageSize,
138
- onRowClick: (_: any, { dataIndex }: any) => {
139
- const item = data.list[dataIndex] as TPricingTableExpanded;
140
- startTransition(() => {
141
- navigate(`/admin/products/${item.id}`);
142
- });
143
- },
144
147
  }}
145
148
  loading={loading}
146
149
  onChange={onTableChange}
@@ -5,13 +5,12 @@ import { Status, api, formatTime, usePaymentContext } from '@blocklet/payment-re
5
5
  import type { TProductExpanded } from '@blocklet/payment-types';
6
6
  import { CircularProgress } from '@mui/material';
7
7
  import { useEffect, useState } from 'react';
8
- import { useNavigate } from 'react-router-dom';
8
+ import { Link } from 'react-router-dom';
9
9
  import useBus from 'use-bus';
10
10
 
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 { useTransitionContext } from '../../../../components/progress-bar';
15
14
  import Table from '../../../../components/table';
16
15
  import { formatProductPrice } from '../../../../libs/util';
17
16
 
@@ -34,8 +33,6 @@ export default function ProductsList() {
34
33
  const persisted = getDurableData(listKey);
35
34
 
36
35
  const { t, locale } = useLocaleContext();
37
- const navigate = useNavigate();
38
- const { startTransition } = useTransitionContext();
39
36
  const { settings } = usePaymentContext();
40
37
  const [search, setSearch] = useState<{ active: string; pageSize: number; page: any; q?: any; o?: any }>({
41
38
  active: '',
@@ -67,13 +64,15 @@ export default function ProductsList() {
67
64
  options: {
68
65
  filter: true,
69
66
  customBodyRenderLite: (_: string, index: number) => {
70
- const product = data.list[index] as TProductExpanded;
67
+ const item = data.list[index] as TProductExpanded;
71
68
  return (
72
- <InfoCard
73
- name={product.name}
74
- description={formatProductPrice(product as any, settings.baseCurrency, locale)}
75
- logo={product.images[0]}
76
- />
69
+ <Link to={`/admin/products/${item.id}`}>
70
+ <InfoCard
71
+ name={item.name}
72
+ description={formatProductPrice(item as any, settings.baseCurrency, locale)}
73
+ logo={item.images[0]}
74
+ />
75
+ </Link>
77
76
  );
78
77
  },
79
78
  },
@@ -84,8 +83,12 @@ export default function ProductsList() {
84
83
  options: {
85
84
  filter: true,
86
85
  customBodyRenderLite: (_: string, index: number) => {
87
- const item = data.list[index];
88
- return <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />;
86
+ const item = data.list[index] as TProductExpanded;
87
+ return (
88
+ <Link to={`/admin/products/${item.id}`}>
89
+ <Status label={item?.active ? 'Active' : 'Archived'} color={item?.active ? 'success' : 'default'} />
90
+ </Link>
91
+ );
89
92
  },
90
93
  },
91
94
  },
@@ -94,8 +97,9 @@ export default function ProductsList() {
94
97
  name: 'created_at',
95
98
  options: {
96
99
  sort: true,
97
- customBodyRender: (e: string) => {
98
- return formatTime(e);
100
+ customBodyRenderLite: (_: string, index: number) => {
101
+ const item = data.list[index] as TProductExpanded;
102
+ return <Link to={`/admin/products/${item.id}`}>{formatTime(item.created_at)}</Link>;
99
103
  },
100
104
  },
101
105
  },
@@ -103,8 +107,10 @@ export default function ProductsList() {
103
107
  label: t('common.updatedAt'),
104
108
  name: 'updated_at',
105
109
  options: {
106
- customBodyRender: (e: string) => {
107
- return formatTime(e);
110
+ sort: true,
111
+ customBodyRenderLite: (_: string, index: number) => {
112
+ const item = data.list[index] as TProductExpanded;
113
+ return <Link to={`/admin/products/${item.id}`}>{formatTime(item.updated_at)}</Link>;
108
114
  },
109
115
  },
110
116
  },
@@ -133,6 +139,7 @@ export default function ProductsList() {
133
139
 
134
140
  return (
135
141
  <Table
142
+ hasRowLink
136
143
  durable={`__${listKey}__`}
137
144
  durableKeys={['page', 'rowsPerPage', 'searchText']}
138
145
  title={<FilterToolbar setSearch={setSearch} search={search} status={['active', 'archived']} />}
@@ -168,12 +175,6 @@ export default function ProductsList() {
168
175
  });
169
176
  }
170
177
  },
171
- onRowClick: (_: any, { dataIndex }: any) => {
172
- const item = data.list[dataIndex] as TProductExpanded;
173
- startTransition(() => {
174
- navigate(`/admin/products/${item.id}`);
175
- });
176
- },
177
178
  }}
178
179
  loading={!data.list}
179
180
  onChange={onTableChange}