payment-kit 1.13.161 → 1.13.162
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/.eslintrc.js +2 -1
- package/api/src/libs/api.ts +18 -29
- package/api/src/routes/customers.ts +12 -7
- package/api/src/routes/invoices.ts +17 -6
- package/api/src/routes/payment-intents.ts +19 -7
- package/api/src/routes/products.ts +2 -2
- package/api/src/routes/refunds.ts +53 -3
- package/api/src/routes/subscription-items.ts +18 -9
- package/api/src/routes/subscriptions.ts +20 -31
- package/api/tests/libs/util.spec.ts +0 -26
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/components/filter-toolbar.tsx +375 -0
- package/src/components/invoice/list.tsx +17 -46
- package/src/components/payment-intent/list.tsx +17 -41
- package/src/components/payment-link/product-select.tsx +47 -28
- package/src/components/refund/list.tsx +52 -26
- package/src/components/subscription/items/usage-records.tsx +1 -0
- package/src/components/subscription/list.tsx +15 -44
- package/src/components/table.tsx +13 -1
- package/src/global.css +1 -1
- package/src/pages/admin/customers/customers/index.tsx +15 -8
- package/src/pages/admin/payments/links/index.tsx +1 -0
- package/src/pages/admin/products/prices/list.tsx +1 -0
- package/src/pages/admin/products/pricing-tables/index.tsx +1 -0
- package/src/pages/admin/products/products/index.tsx +1 -0
- package/src/pages/customer/index.tsx +1 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.162",
|
|
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.
|
|
53
|
+
"@blocklet/payment-react": "1.13.162",
|
|
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.
|
|
113
|
+
"@blocklet/payment-types": "1.13.162",
|
|
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": "
|
|
152
|
+
"gitHead": "e11cae8972d35cde567d4784511aacd33a29c1bc"
|
|
153
153
|
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import { api, usePaymentContext } from '@blocklet/payment-react';
|
|
3
|
+
import type { TCustomer } from '@blocklet/payment-types';
|
|
4
|
+
import { Add, Close } from '@mui/icons-material';
|
|
5
|
+
import { Box, styled } from '@mui/system';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { ProductsProvider } from '../contexts/products';
|
|
9
|
+
import InfoCard from './info-card';
|
|
10
|
+
import { FilterProducts } from './payment-link/product-select';
|
|
11
|
+
|
|
12
|
+
const fetchUserData = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
|
|
13
|
+
const search = new URLSearchParams();
|
|
14
|
+
Object.keys(params).forEach((key) => {
|
|
15
|
+
let v = params[key];
|
|
16
|
+
if (key === 'q') {
|
|
17
|
+
v = Object.entries(v)
|
|
18
|
+
.map((x) => x.join(':'))
|
|
19
|
+
.join(' ');
|
|
20
|
+
}
|
|
21
|
+
search.set(key, String(v));
|
|
22
|
+
});
|
|
23
|
+
return api.get(`api/customers?${search.toString()}`).then((res) => res.data);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const fetchSubscriptionItems = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
|
|
27
|
+
const search = new URLSearchParams();
|
|
28
|
+
Object.keys(params).forEach((key) => {
|
|
29
|
+
const v = params[key];
|
|
30
|
+
search.set(key, String(v));
|
|
31
|
+
});
|
|
32
|
+
return api.get(`api/subscription-items?${search.toString()}`).then((res) => res.data);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type Props = {
|
|
36
|
+
search?: Record<string, any>;
|
|
37
|
+
status?: string[];
|
|
38
|
+
currency?: boolean;
|
|
39
|
+
setSearch: (x: any) => void;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default function FilterTooolbar(props: Props) {
|
|
43
|
+
const { setSearch, search, status, currency } = props;
|
|
44
|
+
const handleSearch = (obj: any) => {
|
|
45
|
+
setSearch({
|
|
46
|
+
...search,
|
|
47
|
+
...obj,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Root>
|
|
53
|
+
<div className="table-toolbar-left">
|
|
54
|
+
<SearchStatus setSearch={handleSearch} status={status} />
|
|
55
|
+
<SearchCustomers setSearch={handleSearch} />
|
|
56
|
+
{currency ? <SearchCurrency setSearch={handleSearch} /> : <SearchProducts setSearch={handleSearch} />}
|
|
57
|
+
</div>
|
|
58
|
+
</Root>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const defaultProps = {
|
|
63
|
+
search: {},
|
|
64
|
+
status: {},
|
|
65
|
+
currency: false,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
FilterTooolbar.defaultProps = defaultProps;
|
|
69
|
+
SearchStatus.defaultProps = defaultProps;
|
|
70
|
+
SearchCurrency.defaultProps = defaultProps;
|
|
71
|
+
SearchProducts.defaultProps = defaultProps;
|
|
72
|
+
SearchCustomers.defaultProps = defaultProps;
|
|
73
|
+
|
|
74
|
+
function SearchStatus({ status = [], setSearch }: Pick<Props, 'status' | 'setSearch'>) {
|
|
75
|
+
const [show, setShow] = useState(false);
|
|
76
|
+
const [display, setDisplay] = useState('');
|
|
77
|
+
const { t } = useLocaleContext();
|
|
78
|
+
return (
|
|
79
|
+
<section
|
|
80
|
+
onClick={() => {
|
|
81
|
+
setShow(!show);
|
|
82
|
+
}}>
|
|
83
|
+
<div className="option-btn">
|
|
84
|
+
{display ? (
|
|
85
|
+
<Close
|
|
86
|
+
sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
|
|
87
|
+
onClick={(e) => {
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
setSearch({
|
|
90
|
+
status: '',
|
|
91
|
+
});
|
|
92
|
+
setDisplay('');
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
) : (
|
|
96
|
+
<Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }} />
|
|
97
|
+
)}
|
|
98
|
+
{t('common.status')}
|
|
99
|
+
<span>{display}</span>
|
|
100
|
+
</div>
|
|
101
|
+
<ul className="status-options" style={{ display: show ? 'block' : 'none' }}>
|
|
102
|
+
{status.map((x: any) => (
|
|
103
|
+
<li
|
|
104
|
+
onClick={() => {
|
|
105
|
+
setSearch({
|
|
106
|
+
status: x,
|
|
107
|
+
});
|
|
108
|
+
setDisplay(x);
|
|
109
|
+
}}
|
|
110
|
+
key={x}>
|
|
111
|
+
{x}
|
|
112
|
+
</li>
|
|
113
|
+
))}
|
|
114
|
+
</ul>
|
|
115
|
+
</section>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function filterCurrency(methods: any) {
|
|
120
|
+
const out: any = {};
|
|
121
|
+
methods.forEach((m: any) => {
|
|
122
|
+
const currencies = m.payment_currencies;
|
|
123
|
+
currencies.forEach((c: any) => {
|
|
124
|
+
out[c.symbol] = c.id;
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function SearchCurrency({ setSearch }: Pick<Props, 'setSearch'>) {
|
|
131
|
+
const [show, setShow] = useState(false);
|
|
132
|
+
const [display, setDisplay] = useState('');
|
|
133
|
+
const { settings } = usePaymentContext();
|
|
134
|
+
const currencies = filterCurrency(settings.paymentMethods);
|
|
135
|
+
const { t } = useLocaleContext();
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<section
|
|
139
|
+
onClick={() => {
|
|
140
|
+
setShow(!show);
|
|
141
|
+
}}>
|
|
142
|
+
<div className="option-btn">
|
|
143
|
+
{display ? (
|
|
144
|
+
<Close
|
|
145
|
+
sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
|
|
146
|
+
onClick={(e) => {
|
|
147
|
+
e.stopPropagation();
|
|
148
|
+
setSearch({
|
|
149
|
+
currency_id: '',
|
|
150
|
+
});
|
|
151
|
+
setDisplay('');
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
) : (
|
|
155
|
+
<Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }} />
|
|
156
|
+
)}
|
|
157
|
+
{t('common.currency')}
|
|
158
|
+
<span>{display}</span>
|
|
159
|
+
</div>
|
|
160
|
+
<ul className="status-options" style={{ display: show ? 'block' : 'none' }}>
|
|
161
|
+
{Object.keys(currencies).map((k: any) => (
|
|
162
|
+
<li
|
|
163
|
+
onClick={() => {
|
|
164
|
+
setSearch({
|
|
165
|
+
currency_id: currencies[k],
|
|
166
|
+
});
|
|
167
|
+
setDisplay(k);
|
|
168
|
+
}}
|
|
169
|
+
key={k}>
|
|
170
|
+
{k}
|
|
171
|
+
</li>
|
|
172
|
+
))}
|
|
173
|
+
</ul>
|
|
174
|
+
</section>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function SearchCustomers({ setSearch }: Pick<Props, 'setSearch'>) {
|
|
179
|
+
const [customers, setCustomers] = useState([]);
|
|
180
|
+
const [show, setShow] = useState(false);
|
|
181
|
+
const [text, setText] = useState('');
|
|
182
|
+
const [display, setDisplay] = useState('');
|
|
183
|
+
const { t } = useLocaleContext();
|
|
184
|
+
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
fetchUserData({
|
|
187
|
+
q: {
|
|
188
|
+
'like-name': text,
|
|
189
|
+
'like-email': text,
|
|
190
|
+
'like-did': text,
|
|
191
|
+
'like-metadata': text,
|
|
192
|
+
'like-phone': text,
|
|
193
|
+
},
|
|
194
|
+
page: 1,
|
|
195
|
+
pageSize: 100,
|
|
196
|
+
}).then((data: any) => {
|
|
197
|
+
setCustomers(data.list);
|
|
198
|
+
});
|
|
199
|
+
}, [text]);
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<section onClick={() => setShow(!show)}>
|
|
203
|
+
<div className="option-btn">
|
|
204
|
+
{display ? (
|
|
205
|
+
<Close
|
|
206
|
+
sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
|
|
207
|
+
onClick={(e) => {
|
|
208
|
+
e.stopPropagation();
|
|
209
|
+
setSearch({
|
|
210
|
+
customer_id: '',
|
|
211
|
+
});
|
|
212
|
+
setDisplay('');
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
) : (
|
|
216
|
+
<Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }} />
|
|
217
|
+
)}
|
|
218
|
+
{t('common.customer')}
|
|
219
|
+
<span>{display}</span>
|
|
220
|
+
</div>
|
|
221
|
+
<ul className="status-options" style={{ display: show ? 'block' : 'none' }} onClick={(e) => e.stopPropagation()}>
|
|
222
|
+
<input
|
|
223
|
+
type="text"
|
|
224
|
+
placeholder="search customers"
|
|
225
|
+
onChange={(e) => {
|
|
226
|
+
setText(e.target.value);
|
|
227
|
+
}}
|
|
228
|
+
/>
|
|
229
|
+
{customers.map((x: any) => (
|
|
230
|
+
<li
|
|
231
|
+
key={x.id}
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setSearch({
|
|
234
|
+
customer_id: x.id,
|
|
235
|
+
});
|
|
236
|
+
setDisplay(x.name);
|
|
237
|
+
}}>
|
|
238
|
+
<InfoCard
|
|
239
|
+
logo={`/.well-known/service/user/avatar/${x.did}?imageFilter=resize&w=48&h=48`}
|
|
240
|
+
name={x.email}
|
|
241
|
+
key={x.id}
|
|
242
|
+
description={`${x.did.slice(0, 6)}...${x.did.slice(-6)}`}
|
|
243
|
+
/>
|
|
244
|
+
</li>
|
|
245
|
+
))}
|
|
246
|
+
</ul>
|
|
247
|
+
</section>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function SearchProducts({ setSearch }: Pick<Props, 'setSearch'>) {
|
|
252
|
+
const [show, setShow] = useState(false);
|
|
253
|
+
const [price, setPrice] = useState({} as any);
|
|
254
|
+
const [display, setDisplay] = useState('');
|
|
255
|
+
const isSubscription = window.location.pathname.includes('subscriptions');
|
|
256
|
+
const { t } = useLocaleContext();
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (!price.id) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
fetchSubscriptionItems({
|
|
263
|
+
price_id: price.id,
|
|
264
|
+
}).then((data) => {
|
|
265
|
+
const str = data.list.map((x: any) => x.subscription_id).join(',');
|
|
266
|
+
const key = isSubscription ? 'id' : 'subscription_id';
|
|
267
|
+
setSearch({
|
|
268
|
+
q: {
|
|
269
|
+
[key]: str,
|
|
270
|
+
},
|
|
271
|
+
page: 1,
|
|
272
|
+
pageSize: 100,
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}, [price]);
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<section onClick={() => setShow(!show)}>
|
|
279
|
+
<div className="option-btn">
|
|
280
|
+
{display ? (
|
|
281
|
+
<Close
|
|
282
|
+
sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }}
|
|
283
|
+
onClick={(e) => {
|
|
284
|
+
e.stopPropagation();
|
|
285
|
+
setSearch({
|
|
286
|
+
q: '',
|
|
287
|
+
});
|
|
288
|
+
setDisplay('');
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
) : (
|
|
292
|
+
<Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.2rem' }} />
|
|
293
|
+
)}
|
|
294
|
+
{t('admin.subscription.product')}
|
|
295
|
+
<span>{display}</span>
|
|
296
|
+
</div>
|
|
297
|
+
<ul className="status-options" style={{ display: show ? 'block' : 'none' }} onClick={(e) => e.stopPropagation()}>
|
|
298
|
+
<ProductsProvider>
|
|
299
|
+
<FilterProducts
|
|
300
|
+
hasSelected={() => false}
|
|
301
|
+
onSelect={(p: any) => {
|
|
302
|
+
setPrice(p);
|
|
303
|
+
setDisplay(`${p.productName}(${p.displayPrice})`);
|
|
304
|
+
}}
|
|
305
|
+
/>
|
|
306
|
+
</ProductsProvider>
|
|
307
|
+
</ul>
|
|
308
|
+
</section>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const Root = styled(Box)`
|
|
313
|
+
.table-toolbar-left {
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: cneter;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.table-toolbar-left section {
|
|
319
|
+
position: relative;
|
|
320
|
+
list-style: none;
|
|
321
|
+
font-size: 14px;
|
|
322
|
+
font-weight: normal;
|
|
323
|
+
padding: 0 5px;
|
|
324
|
+
cursor: pointer;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.option-btn {
|
|
328
|
+
display: flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
border-radius: 25px;
|
|
331
|
+
background: #f6f6f6;
|
|
332
|
+
padding: 0 10px;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.option-btn span {
|
|
336
|
+
color: #3773f2;
|
|
337
|
+
padding: 0 3px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.custom-toobar-title-inner {
|
|
341
|
+
line-height: normal;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.status-options {
|
|
345
|
+
position: absolute;
|
|
346
|
+
padding: 10px;
|
|
347
|
+
background: #fff;
|
|
348
|
+
border: 1px solid #eee;
|
|
349
|
+
border-radius: 5px;
|
|
350
|
+
z-index: 999999;
|
|
351
|
+
top: 25px;
|
|
352
|
+
display: block;
|
|
353
|
+
max-height: 500px;
|
|
354
|
+
overflow-y: auto;
|
|
355
|
+
overflow-x: hidden;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.status-options input {
|
|
359
|
+
width: 100%;
|
|
360
|
+
border: 1px solid #eee;
|
|
361
|
+
padding: 5px 10px;
|
|
362
|
+
border-radius: 20px;
|
|
363
|
+
margin-bottom: 10px;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.status-options li {
|
|
367
|
+
list-style: none;
|
|
368
|
+
padding: 5px;
|
|
369
|
+
line-height: normal;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.custom-toobar-title-inner span {
|
|
373
|
+
overflow: auto;
|
|
374
|
+
}
|
|
375
|
+
`;
|
|
@@ -3,13 +3,13 @@ import { getDurableData } from '@arcblock/ux/lib/Datatable';
|
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
4
|
import { Status, api, formatTime, getInvoiceStatusColor } from '@blocklet/payment-react';
|
|
5
5
|
import type { TInvoiceExpanded } from '@blocklet/payment-types';
|
|
6
|
-
import { CircularProgress,
|
|
6
|
+
import { CircularProgress, Typography } from '@mui/material';
|
|
7
7
|
import { fromUnitToToken } from '@ocap/util';
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
9
9
|
import { useNavigate } from 'react-router-dom';
|
|
10
10
|
|
|
11
|
-
import { debounce } from '../../libs/util';
|
|
12
11
|
import CustomerLink from '../customer/link';
|
|
12
|
+
import FilterTooolbar from '../filter-toolbar';
|
|
13
13
|
import Table from '../table';
|
|
14
14
|
import InvoiceActions from './action';
|
|
15
15
|
|
|
@@ -18,15 +18,13 @@ const fetchData = (params: Record<string, any> = {}): Promise<{ list: TInvoiceEx
|
|
|
18
18
|
Object.keys(params).forEach((key) => {
|
|
19
19
|
let v = params[key];
|
|
20
20
|
if (key === 'q') {
|
|
21
|
-
v = Object.entries(
|
|
21
|
+
v = Object.entries(v)
|
|
22
22
|
.map((x) => x.join(':'))
|
|
23
23
|
.join(' ');
|
|
24
24
|
}
|
|
25
25
|
search.set(key, String(v));
|
|
26
26
|
});
|
|
27
|
-
return
|
|
28
|
-
? api.get(`api/invoices/search?${search.toString()}`).then((res) => res.data)
|
|
29
|
-
: api.get(`/api/invoices?${search.toString()}`).then((res) => res.data);
|
|
27
|
+
return api.get(`/api/invoices?${search.toString()}`).then((res) => res.data);
|
|
30
28
|
};
|
|
31
29
|
|
|
32
30
|
type SearchProps = {
|
|
@@ -36,6 +34,7 @@ type SearchProps = {
|
|
|
36
34
|
customer_id?: string;
|
|
37
35
|
subscription_id?: string;
|
|
38
36
|
q?: any;
|
|
37
|
+
o?: string;
|
|
39
38
|
};
|
|
40
39
|
|
|
41
40
|
type ListProps = {
|
|
@@ -69,7 +68,6 @@ const getListKey = (props: ListProps) => {
|
|
|
69
68
|
InvoiceList.defaultProps = {
|
|
70
69
|
features: {
|
|
71
70
|
customer: true,
|
|
72
|
-
filter: true,
|
|
73
71
|
},
|
|
74
72
|
customer_id: '',
|
|
75
73
|
subscription_id: '',
|
|
@@ -102,7 +100,7 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
102
100
|
});
|
|
103
101
|
|
|
104
102
|
useEffect(() => {
|
|
105
|
-
|
|
103
|
+
refresh();
|
|
106
104
|
}, [search]);
|
|
107
105
|
|
|
108
106
|
if (!data.list) {
|
|
@@ -131,7 +129,6 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
131
129
|
name: 'status',
|
|
132
130
|
width: 60,
|
|
133
131
|
options: {
|
|
134
|
-
filter: true,
|
|
135
132
|
customBodyRenderLite: (_: string, index: number) => {
|
|
136
133
|
const item = data.list[index] as TInvoiceExpanded;
|
|
137
134
|
return <Status label={item?.status} color={getInvoiceStatusColor(item?.status)} />;
|
|
@@ -146,7 +143,6 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
146
143
|
label: t('common.description'),
|
|
147
144
|
name: 'description',
|
|
148
145
|
options: {
|
|
149
|
-
filter: true,
|
|
150
146
|
customBodyRenderLite: (_: string, index: number) => {
|
|
151
147
|
const item = data.list[index];
|
|
152
148
|
return item?.description || item?.id;
|
|
@@ -166,6 +162,8 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
166
162
|
label: t('common.createdAt'),
|
|
167
163
|
name: 'created_at',
|
|
168
164
|
options: {
|
|
165
|
+
sort: true,
|
|
166
|
+
responsive: 'vertical',
|
|
169
167
|
customBodyRender: (e: string) => {
|
|
170
168
|
return formatTime(e);
|
|
171
169
|
},
|
|
@@ -193,7 +191,6 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
193
191
|
name: 'customer_id',
|
|
194
192
|
width: 60,
|
|
195
193
|
options: {
|
|
196
|
-
filter: true,
|
|
197
194
|
customBodyRenderLite: (_: string, index: number) => {
|
|
198
195
|
const item = data.list[index] as TInvoiceExpanded;
|
|
199
196
|
return <CustomerLink customer={item.customer} />;
|
|
@@ -231,34 +228,19 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
231
228
|
navigate(`/admin/billing/${item.id}`);
|
|
232
229
|
}
|
|
233
230
|
},
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
pageSize: 100,
|
|
241
|
-
page: 1,
|
|
242
|
-
});
|
|
243
|
-
} else if (filterList && filterList[index]) {
|
|
244
|
-
// 这里暂时没有更好的取值方式
|
|
245
|
-
const value = filterList[index][0];
|
|
246
|
-
setSearch({
|
|
247
|
-
q: {
|
|
248
|
-
...search.q,
|
|
249
|
-
[key]: encodeURIComponent(value),
|
|
250
|
-
},
|
|
251
|
-
pageSize: 100,
|
|
252
|
-
page: 1,
|
|
253
|
-
});
|
|
254
|
-
}
|
|
231
|
+
onColumnSortChange(_: any, order: any) {
|
|
232
|
+
setSearch({
|
|
233
|
+
...search,
|
|
234
|
+
q: search.q || {},
|
|
235
|
+
o: order,
|
|
236
|
+
});
|
|
255
237
|
},
|
|
256
238
|
onSearchChange: (text: string) => {
|
|
257
239
|
if (text) {
|
|
258
240
|
setSearch({
|
|
259
241
|
q: {
|
|
260
|
-
'like-
|
|
261
|
-
'like-
|
|
242
|
+
'like-description': text,
|
|
243
|
+
'like-metadata': text,
|
|
262
244
|
'like-number': text,
|
|
263
245
|
},
|
|
264
246
|
pageSize: 100,
|
|
@@ -278,18 +260,7 @@ export default function InvoiceList({ customer_id, subscription_id, features, st
|
|
|
278
260
|
toolbar={features?.toolbar}
|
|
279
261
|
footer={features?.footer}
|
|
280
262
|
title={
|
|
281
|
-
<
|
|
282
|
-
<ToggleButtonGroup
|
|
283
|
-
value={search.status}
|
|
284
|
-
onChange={(_, value) => setSearch((x) => ({ ...x, status: value }))}
|
|
285
|
-
exclusive>
|
|
286
|
-
<ToggleButton value="">All</ToggleButton>
|
|
287
|
-
<ToggleButton value="draft">Draft</ToggleButton>
|
|
288
|
-
<ToggleButton value="uncollectible">Outstanding</ToggleButton>
|
|
289
|
-
<ToggleButton value="open">Past Due</ToggleButton>
|
|
290
|
-
<ToggleButton value="paid">Paid</ToggleButton>
|
|
291
|
-
</ToggleButtonGroup>
|
|
292
|
-
</div>
|
|
263
|
+
<FilterTooolbar setSearch={setSearch} search={search} status={['paid', 'open', 'uncollectible']} currency />
|
|
293
264
|
}
|
|
294
265
|
/>
|
|
295
266
|
);
|
|
@@ -3,13 +3,14 @@ import { getDurableData } from '@arcblock/ux/lib/Datatable';
|
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
4
|
import { Status, api, formatTime, getPaymentIntentStatusColor } from '@blocklet/payment-react';
|
|
5
5
|
import type { TPaymentIntentExpanded } from '@blocklet/payment-types';
|
|
6
|
-
import { CircularProgress,
|
|
6
|
+
import { CircularProgress, Typography } from '@mui/material';
|
|
7
7
|
import { fromUnitToToken } from '@ocap/util';
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
9
9
|
import { useNavigate } from 'react-router-dom';
|
|
10
10
|
|
|
11
11
|
import { debounce } from '../../libs/util';
|
|
12
12
|
import CustomerLink from '../customer/link';
|
|
13
|
+
import FilterTooolbar from '../filter-toolbar';
|
|
13
14
|
import Table from '../table';
|
|
14
15
|
import PaymentIntentActions from './actions';
|
|
15
16
|
|
|
@@ -18,15 +19,13 @@ const fetchData = (params: Record<string, any> = {}): Promise<{ list: TPaymentIn
|
|
|
18
19
|
Object.keys(params).forEach((key) => {
|
|
19
20
|
let v = params[key];
|
|
20
21
|
if (key === 'q') {
|
|
21
|
-
v = Object.entries(
|
|
22
|
+
v = Object.entries(v)
|
|
22
23
|
.map((x) => x.join(':'))
|
|
23
24
|
.join(' ');
|
|
24
25
|
}
|
|
25
26
|
search.set(key, String(v));
|
|
26
27
|
});
|
|
27
|
-
return
|
|
28
|
-
? api.get(`api/payment-intents/search?${search.toString()}`).then((res) => res.data)
|
|
29
|
-
: api.get(`/api/payment-intents?${search.toString()}`).then((res) => res.data);
|
|
28
|
+
return api.get(`/api/payment-intents?${search.toString()}`).then((res) => res.data);
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
type SearchProps = {
|
|
@@ -36,6 +35,7 @@ type SearchProps = {
|
|
|
36
35
|
customer_id?: string;
|
|
37
36
|
invoice_id?: string;
|
|
38
37
|
q?: any;
|
|
38
|
+
o?: any;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
type ListProps = {
|
|
@@ -142,6 +142,8 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
142
142
|
label: t('common.createdAt'),
|
|
143
143
|
name: 'created_at',
|
|
144
144
|
options: {
|
|
145
|
+
sort: true,
|
|
146
|
+
filterType: 'dropdown',
|
|
145
147
|
customBodyRender: (e: string) => {
|
|
146
148
|
return formatTime(e);
|
|
147
149
|
},
|
|
@@ -193,34 +195,12 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
193
195
|
count: data.count,
|
|
194
196
|
page: search.page - 1,
|
|
195
197
|
rowsPerPage: search.pageSize,
|
|
196
|
-
onFilterChange: (key: any, filterList: any, type: any, index: number) => {
|
|
197
|
-
if (type === 'reset') {
|
|
198
|
-
setSearch({
|
|
199
|
-
status: '',
|
|
200
|
-
customer_id,
|
|
201
|
-
invoice_id,
|
|
202
|
-
pageSize: 100,
|
|
203
|
-
page: 1,
|
|
204
|
-
});
|
|
205
|
-
} else if (filterList && filterList[index]) {
|
|
206
|
-
// 这里暂时没有更好的取值方式
|
|
207
|
-
const value = filterList[index][0];
|
|
208
|
-
setSearch({
|
|
209
|
-
q: {
|
|
210
|
-
...search.q,
|
|
211
|
-
[key]: encodeURIComponent(value),
|
|
212
|
-
},
|
|
213
|
-
pageSize: 100,
|
|
214
|
-
page: 1,
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
198
|
onSearchChange: (text: string) => {
|
|
219
199
|
if (text) {
|
|
220
200
|
setSearch({
|
|
221
201
|
q: {
|
|
222
|
-
'like-
|
|
223
|
-
'like-
|
|
202
|
+
'like-description': text,
|
|
203
|
+
'like-metadata': text,
|
|
224
204
|
},
|
|
225
205
|
pageSize: 100,
|
|
226
206
|
page: 1,
|
|
@@ -235,6 +215,13 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
235
215
|
});
|
|
236
216
|
}
|
|
237
217
|
},
|
|
218
|
+
onColumnSortChange(_: any, order: any) {
|
|
219
|
+
setSearch({
|
|
220
|
+
...search,
|
|
221
|
+
q: search.q || {},
|
|
222
|
+
o: order,
|
|
223
|
+
});
|
|
224
|
+
},
|
|
238
225
|
onRowClick: (_: any, { dataIndex }: any) => {
|
|
239
226
|
const item = data.list[dataIndex] as TPaymentIntentExpanded;
|
|
240
227
|
navigate(`/admin/payments/${item.id}`);
|
|
@@ -243,18 +230,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
|
|
|
243
230
|
toolbar={features?.toolbar}
|
|
244
231
|
footer={features?.footer}
|
|
245
232
|
title={
|
|
246
|
-
<
|
|
247
|
-
<ToggleButtonGroup
|
|
248
|
-
value={search.status}
|
|
249
|
-
onChange={(_, value) => setSearch((x) => ({ ...x, status: value }))}
|
|
250
|
-
exclusive>
|
|
251
|
-
<ToggleButton value="">All</ToggleButton>
|
|
252
|
-
<ToggleButton value="succeeded">Succeeded</ToggleButton>
|
|
253
|
-
<ToggleButton value="canceled">Canceled</ToggleButton>
|
|
254
|
-
<ToggleButton value="requires_capture">Uncaptured</ToggleButton>
|
|
255
|
-
<ToggleButton value="requires_action,requires_confirmation,requires_payment_method">Failed</ToggleButton>
|
|
256
|
-
</ToggleButtonGroup>
|
|
257
|
-
</div>
|
|
233
|
+
<FilterTooolbar setSearch={setSearch} search={search} status={['succeeded', 'requires_action']} currency />
|
|
258
234
|
}
|
|
259
235
|
/>
|
|
260
236
|
);
|