payment-kit 1.15.23 → 1.15.25

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.
@@ -0,0 +1,127 @@
1
+ import { formatTime } from '@blocklet/payment-react';
2
+ import { useEffect } from 'react';
3
+ import type { InvoicePDFProps } from './types';
4
+ import { composeStyles } from './utils';
5
+ import { getInvoiceRows } from '../invoice/table';
6
+ import { applyStyles } from './styles';
7
+
8
+ export function InvoiceTemplate({ data, t }: InvoicePDFProps) {
9
+ const { detail, summary } = getInvoiceRows(data);
10
+
11
+ useEffect(() => {
12
+ applyStyles();
13
+ }, []);
14
+
15
+ return (
16
+ <div id="invoice-template" style={composeStyles('template')}>
17
+ {/* Header */}
18
+ <div
19
+ style={{
20
+ width: '100%',
21
+ display: 'flex',
22
+ justifyContent: 'space-between',
23
+ alignItems: 'center',
24
+ gap: '20px',
25
+ marginBottom: '40px',
26
+ }}>
27
+ <div style={composeStyles('fs-20 bold')}>{t('admin.invoice.name')}</div>
28
+ <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
29
+ <img src={window.blocklet.appLogo.split('?')[0]} style={composeStyles('logo')} alt="logo" />
30
+ <span style={composeStyles('block p-10')}>{window.blocklet.appName}</span>
31
+ </div>
32
+ </div>
33
+
34
+ {/* Bill Info */}
35
+ <div style={composeStyles('flex mt-60')}>
36
+ <div style={composeStyles('w-60')}>
37
+ <span style={composeStyles('bold dark mb-5')}>{t('admin.invoice.billTo')}</span>
38
+ <span style={composeStyles('block mb-5')}>{data.customer.name}</span>
39
+ <span style={composeStyles('bold fs-12')}>{`DID:ABT:${data.customer.did}`}</span>
40
+ </div>
41
+ <div style={composeStyles('w-40')}>
42
+ <div style={composeStyles('flex mb-5')}>
43
+ <div style={composeStyles('w-40')}>
44
+ <span style={composeStyles('bold')}>{t('admin.invoice.number')}</span>
45
+ </div>
46
+ <div style={composeStyles('w-60')}>
47
+ <span style={composeStyles('gray')}>{data.number}</span>
48
+ </div>
49
+ </div>
50
+ <div style={composeStyles('flex mb-5')}>
51
+ <div style={composeStyles('w-40')}>
52
+ <span style={composeStyles('bold')}>{t('admin.invoice.paidAt')}</span>
53
+ </div>
54
+ <div style={composeStyles('w-60')}>
55
+ <span style={composeStyles('gray')}>{formatTime(data.period_start * 1000)}</span>
56
+ </div>
57
+ </div>
58
+ <div style={composeStyles('flex mb-5')}>
59
+ <div style={composeStyles('w-40')}>
60
+ <span style={composeStyles('bold')}>{t('admin.invoice.dueDate')}</span>
61
+ </div>
62
+ <div style={composeStyles('w-60')}>
63
+ <span style={composeStyles('gray')}>{formatTime(data.period_end * 1000)}</span>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ {/* Table Header */}
70
+ <div style={composeStyles('mt-40 row flex')}>
71
+ <div style={composeStyles('w-48 p-4-8')}>
72
+ <span style={composeStyles('bold')}>{t('admin.subscription.product')}</span>
73
+ </div>
74
+ <div style={composeStyles('w-17 p-4-8')}>
75
+ <span style={composeStyles('bold right')}>{t('common.quantity')}</span>
76
+ </div>
77
+ <div style={composeStyles('w-17 p-4-8')}>
78
+ <span style={composeStyles('bold right')}>{t('payment.customer.invoice.unitPrice')}</span>
79
+ </div>
80
+ <div style={composeStyles('w-18 p-4-8')}>
81
+ <span style={composeStyles('bold right')}>{t('common.amount')}</span>
82
+ </div>
83
+ </div>
84
+
85
+ {/* Table Content */}
86
+ {detail.map((line) => (
87
+ <div key={line.id} style={composeStyles('row flex')}>
88
+ <div style={composeStyles('w-48 p-4-8 pb-15')}>
89
+ <span style={composeStyles('dark')}>{line.product}</span>
90
+ </div>
91
+ <div style={composeStyles('w-17 p-4-8 pb-15')}>
92
+ <span style={composeStyles('dark right')}>{line.quantity}</span>
93
+ </div>
94
+ <div style={composeStyles('w-17 p-4-8 pb-15')}>
95
+ <span style={composeStyles('dark right')}>
96
+ {line.price ? `${line.price} ${data.paymentCurrency.symbol}` : ''}
97
+ </span>
98
+ </div>
99
+ <div style={composeStyles('w-18 p-4-8 pb-15')}>
100
+ <span style={composeStyles('dark right')}>
101
+ {line.amount} {data.paymentCurrency.symbol}
102
+ </span>
103
+ </div>
104
+ </div>
105
+ ))}
106
+
107
+ {/* Summary */}
108
+ <div style={composeStyles('flex')}>
109
+ <div style={composeStyles('w-60 mt-20')} />
110
+ <div style={composeStyles('w-40 mt-20')}>
111
+ {summary.map((line) => (
112
+ <div style={composeStyles('flex')} key={line.key}>
113
+ <div style={composeStyles('w-60 p-5')}>
114
+ <span style={composeStyles('bold')}>{t(line.key)}</span>
115
+ </div>
116
+ <div style={composeStyles('w-40 p-5')}>
117
+ <span style={composeStyles('right bold dark')}>
118
+ {line.value} {data.paymentCurrency.symbol}
119
+ </span>
120
+ </div>
121
+ </div>
122
+ ))}
123
+ </div>
124
+ </div>
125
+ </div>
126
+ );
127
+ }
@@ -0,0 +1,11 @@
1
+ import type { TInvoiceExpanded } from '@blocklet/payment-types';
2
+ import type { CSSProperties } from 'react';
3
+
4
+ export interface InvoiceStyles {
5
+ [key: string]: CSSProperties;
6
+ }
7
+
8
+ export interface InvoicePDFProps {
9
+ data: TInvoiceExpanded;
10
+ t: (key: string) => string;
11
+ }
@@ -0,0 +1,41 @@
1
+ import { getPrefix } from '@blocklet/payment-react';
2
+ import { joinURL } from 'ufo';
3
+ import { pdfStyles } from './styles';
4
+
5
+ const fontUrl = joinURL(getPrefix(), '/fonts/noto-sans-sc-chinese-simplified-500-normal.ttf');
6
+ export const fontFamily = 'Noto Sans SC';
7
+
8
+ export async function loadFont(): Promise<boolean> {
9
+ try {
10
+ const font = new FontFace(fontFamily, `url(${fontUrl})`);
11
+ const loadedFont = await font.load();
12
+ document.fonts.add(loadedFont);
13
+ return true;
14
+ } catch (error) {
15
+ console.error('Font loading failed:', error);
16
+ return false;
17
+ }
18
+ }
19
+
20
+ export function loadImage(url: string): Promise<HTMLImageElement> {
21
+ return new Promise((resolve, reject) => {
22
+ const img = new Image();
23
+ img.crossOrigin = 'anonymous';
24
+ img.onload = () => resolve(img);
25
+ img.onerror = reject;
26
+ img.src = url;
27
+ });
28
+ }
29
+
30
+ export function composeStyles(classNames: string): React.CSSProperties {
31
+ return classNames
32
+ .split(' ')
33
+ .filter(Boolean)
34
+ .reduce(
35
+ (acc, className) => ({
36
+ ...acc,
37
+ ...pdfStyles[className],
38
+ }),
39
+ {}
40
+ );
41
+ }
@@ -230,9 +230,13 @@ export function ProductPaymentSettings({ product, prices }: { product: TProduct;
230
230
  expanded
231
231
  trigger={<Typography variant="h6">{product.name}</Typography>}
232
232
  style={{ py: 1 }}>
233
+ {/* @ts-ignore */}
233
234
  <Tabs
235
+ // @ts-ignore
234
236
  tabs={tabs}
237
+ // @ts-ignore
235
238
  current={current}
239
+ // @ts-ignore
236
240
  onChange={(v: string) => setCurrent(v)}
237
241
  style={{ width: '100%' }}
238
242
  scrollButtons="auto"
@@ -1,9 +1,13 @@
1
1
  import { Avatar, Box, Button } from '@mui/material';
2
2
  import { Stack, styled } from '@mui/system';
3
- import { lazy, useCallback, useEffect, useRef } from 'react';
3
+ import { lazy, Suspense, useCallback, useEffect, useRef } from 'react';
4
4
  import { CloudUpload, Delete, Edit } from '@mui/icons-material';
5
5
 
6
- const UploaderComponent = lazy(() => import('@blocklet/uploader/react').then((res) => ({ default: res.Uploader })));
6
+ const UploaderComponent = lazy(() =>
7
+ import(/* webpackChunkName: "blocklet-uploader" */ '@blocklet/uploader').then((res) => ({
8
+ default: res.Uploader,
9
+ }))
10
+ );
7
11
 
8
12
  type Props = {
9
13
  onUploaded: (result: any) => void;
@@ -95,30 +99,32 @@ export default function Uploader({ onUploaded, preview, maxFileSize, maxNumberOf
95
99
  </Div>
96
100
  )}
97
101
 
98
- <UploaderComponent
99
- // @ts-ignore
100
- ref={uploaderRef}
101
- popup
102
- onUploadFinish={(result: any) => onUploaded({ url: result.data.url })}
103
- uploadedProps={{
104
- onSelectedFiles: (files: any[]) => {
105
- if (files.length) {
106
- onUploaded({ url: files[0].fileUrl });
107
- }
108
- },
109
- }}
110
- coreProps={{
111
- restrictions: {
112
- allowedFileExts,
113
- maxFileSize,
114
- maxNumberOfFiles,
115
- },
116
- }}
117
- apiPathProps={{
118
- uploader: '/api/uploads',
119
- companion: '/api/companion',
120
- }}
121
- />
102
+ <Suspense fallback={null}>
103
+ <UploaderComponent
104
+ // @ts-ignore
105
+ ref={uploaderRef}
106
+ popup
107
+ onUploadFinish={(result: any) => onUploaded({ url: result.data.url })}
108
+ uploadedProps={{
109
+ onSelectedFiles: (files: any[]) => {
110
+ if (files.length) {
111
+ onUploaded({ url: files[0].fileUrl });
112
+ }
113
+ },
114
+ }}
115
+ coreProps={{
116
+ restrictions: {
117
+ allowedFileExts,
118
+ maxFileSize,
119
+ maxNumberOfFiles,
120
+ },
121
+ }}
122
+ apiPathProps={{
123
+ uploader: '/api/uploads',
124
+ companion: '/api/companion',
125
+ }}
126
+ />
127
+ </Suspense>
122
128
  </>
123
129
  );
124
130
  }
@@ -168,10 +168,12 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
168
168
  <Typography variant="h6">{event_id ? selected.endpoint.url : selected.event.type}</Typography>
169
169
  <Box>
170
170
  <Typography variant="h6">Response ({selected.response_status})</Typography>
171
+ {/* @ts-ignore */}
171
172
  <CodeBlock language="json">{JSON.stringify(selected.response_body, null, 2)}</CodeBlock>
172
173
  </Box>
173
174
  <Box>
174
175
  <Typography variant="h6">Request</Typography>
176
+ {/* @ts-ignore */}
175
177
  <CodeBlock language="json">{JSON.stringify(selected.event, null, 2)}</CodeBlock>
176
178
  </Box>
177
179
  </Stack>
@@ -266,6 +268,7 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
266
268
  </Popper>
267
269
  </>
268
270
  </Stack>
271
+ {/* @ts-ignore */}
269
272
  <CodeBlock language="json">{JSON.stringify(event.data, null, 2)}</CodeBlock>
270
273
  </Box>
271
274
  )}
@@ -46,9 +46,13 @@ export default function BillingIndex() {
46
46
  {/* <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
47
47
  {t('admin.billing')}
48
48
  </Typography> */}
49
+ {/* @ts-ignore */}
49
50
  <Tabs
51
+ // @ts-ignore
50
52
  tabs={tabs}
53
+ // @ts-ignore
51
54
  current={page}
55
+ // @ts-ignore
52
56
  onChange={onTabChange}
53
57
  scrollButtons="auto"
54
58
  sx={{
@@ -33,9 +33,13 @@ export default function CustomerIndex() {
33
33
 
34
34
  return (
35
35
  <div>
36
+ {/* @ts-ignore */}
36
37
  <Tabs
38
+ // @ts-ignore
37
39
  tabs={tabs}
40
+ // @ts-ignore
38
41
  current={page}
42
+ // @ts-ignore
39
43
  onChange={onTabChange}
40
44
  scrollButtons="auto"
41
45
  variant="scrollable"
@@ -50,7 +50,16 @@ export default function DevelopersIndex() {
50
50
  <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
51
51
  {t('admin.developers')}
52
52
  </Typography>
53
- <Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
53
+ {/* @ts-ignore */}
54
+ <Tabs
55
+ // @ts-ignore
56
+ tabs={tabs}
57
+ // @ts-ignore
58
+ current={page}
59
+ // @ts-ignore
60
+ onChange={onTabChange}
61
+ scrollButtons="auto"
62
+ />
54
63
  {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
55
64
  </div>
56
65
  );
@@ -75,9 +75,13 @@ function Admin() {
75
75
  <ProgressBar pending={isPending} />
76
76
  <Stack direction="row" alignItems="center" justifyContent="end" flexWrap="wrap" spacing={1} sx={{ mt: 1, pb: 2 }}>
77
77
  {/* <Box>{renderNav(group, onTabChange, group1)}</Box> */}
78
+ {/* @ts-ignore */}
78
79
  <Tabs
80
+ // @ts-ignore
79
81
  tabs={group1}
82
+ // @ts-ignore
80
83
  current={group}
84
+ // @ts-ignore
81
85
  onChange={onTabChange}
82
86
  scrollButtons="auto"
83
87
  variant="scrollable"
@@ -49,9 +49,13 @@ export default function PaymentIndex() {
49
49
 
50
50
  return (
51
51
  <div>
52
+ {/* @ts-ignore */}
52
53
  <Tabs
54
+ // @ts-ignore
53
55
  tabs={tabs}
56
+ // @ts-ignore
54
57
  current={page}
58
+ // @ts-ignore
55
59
  onChange={onTabChange}
56
60
  scrollButtons="auto"
57
61
  variant="scrollable"
@@ -66,9 +66,13 @@ export default function Products() {
66
66
  return (
67
67
  <>
68
68
  <Stack direction="row" alignItems="flex-start" justifyContent="end" flexWrap="wrap" spacing={1}>
69
+ {/* @ts-ignore */}
69
70
  <Tabs
71
+ // @ts-ignore
70
72
  tabs={tabs}
73
+ // @ts-ignore
71
74
  current={page}
75
+ // @ts-ignore
72
76
  onChange={(newTab: string) => startTransition(() => navigate(`/admin/products/${newTab}`))}
73
77
  scrollButtons="auto"
74
78
  variant="scrollable"
@@ -153,9 +153,13 @@ export default function CreatePaymentLink() {
153
153
  <Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
154
154
  {t('common.setup')}
155
155
  </Typography>
156
+ {/* @ts-ignore */}
156
157
  <Tabs
158
+ // @ts-ignore
157
159
  tabs={tabs}
160
+ // @ts-ignore
158
161
  current={current}
162
+ // @ts-ignore
159
163
  onChange={(v: string) => setCurrent(v)}
160
164
  style={{ width: '100%' }}
161
165
  scrollButtons="auto"
@@ -42,9 +42,13 @@ export default function SettingsIndex() {
42
42
  return (
43
43
  <>
44
44
  <Stack direction="row" alignItems="flex-start" justifyContent="end" flexWrap="wrap" spacing={1}>
45
+ {/* @ts-ignore */}
45
46
  <Tabs
47
+ // @ts-ignore
46
48
  tabs={tabs}
49
+ // @ts-ignore
47
50
  current={page}
51
+ // @ts-ignore
48
52
  onChange={onTabChange}
49
53
  scrollButtons="auto"
50
54
  variant="scrollable"
@@ -22,11 +22,11 @@ import {
22
22
  Grid,
23
23
  MenuItem,
24
24
  Select,
25
- SelectChangeEvent,
26
25
  Stack,
27
26
  Tooltip,
28
27
  Typography,
29
28
  } from '@mui/material';
29
+ import type { SelectChangeEvent } from '@mui/material/Select';
30
30
  import { styled, SxProps } from '@mui/system';
31
31
  import { useSetState } from 'ahooks';
32
32
  import { flatten, isEmpty } from 'lodash';
package/vite.config.ts CHANGED
@@ -4,18 +4,59 @@ import { defineConfig } from 'vite';
4
4
  import { createBlockletPlugin } from 'vite-plugin-blocklet';
5
5
  import svgr from 'vite-plugin-svgr';
6
6
  import tsconfigPaths from 'vite-tsconfig-paths';
7
+ import { visualizer } from 'rollup-plugin-visualizer';
7
8
  import path from 'path';
8
9
 
9
10
  // https://vitejs.dev/config/
10
11
  export default defineConfig(({ mode }) => {
12
+ const isProduction = mode === 'production';
11
13
  return {
12
- plugins: [tsconfigPaths(), react(), createBlockletPlugin(), svgr()],
14
+ plugins: [
15
+ tsconfigPaths(),
16
+ react({
17
+ babel: {
18
+ plugins: isProduction ? [['lodash']] : [],
19
+ },
20
+ }),
21
+ createBlockletPlugin(),
22
+ svgr(),
23
+ process.env.ANALYZE && visualizer({ open: true, gzipSize: true, brotliSize: true }),
24
+ ].filter(Boolean),
25
+
13
26
  build: {
14
- // 禁止 preload 可以解决 js 的请求没有 referer 的问题
15
27
  cssCodeSplit: false,
28
+ minify: 'terser',
29
+ terserOptions: {
30
+ compress: {
31
+ drop_debugger: true,
32
+ pure_funcs: ['assert', 'debug'],
33
+ },
34
+ },
35
+ reportCompressedSize: false,
36
+ chunkSizeWarningLimit: 1000,
16
37
  commonjsOptions: {
38
+ include: [/node_modules/],
17
39
  transformMixedEsModules: true,
18
40
  },
41
+ rollupOptions: {
42
+ external: [],
43
+ output: {
44
+ manualChunks: {
45
+ 'vendor-react': ['react', 'react-dom', 'react-router-dom'],
46
+ 'vendor-mui': ['@mui/material', '@mui/system', '@mui/icons-material'],
47
+ utils: ['dayjs', 'numbro', 'bn.js'],
48
+ hooks: ['ahooks', 'use-bus'],
49
+ 'vendor-arcblock': ['@arcblock/did-connect'],
50
+ },
51
+ },
52
+ },
53
+ },
54
+ optimizeDeps: {
55
+ include: ['react', 'react-dom', 'react/jsx-runtime'],
56
+ esbuildOptions: {
57
+ mainFields: ['module', 'main'],
58
+ resolveExtensions: ['.ts', '.tsx', '.js', '.jsx'],
59
+ },
19
60
  },
20
61
  ...(mode === 'development' && {
21
62
  resolve: {
@@ -1,173 +0,0 @@
1
- const compose = (classes: string) => {
2
- const css = {};
3
-
4
- const classesArray: string[] = classes.replace(/\s+/g, ' ').split(' ');
5
-
6
- classesArray.forEach((className) => {
7
- if (typeof styles[className] !== undefined) {
8
- Object.assign(css, styles[className]);
9
- }
10
- });
11
-
12
- return css;
13
- };
14
-
15
- const colorDark = '#222';
16
- const colorDark2 = '#888';
17
- const colorGray = '#e3e3e3';
18
- const colorWhite = '#fff';
19
-
20
- const styles: any = {
21
- dark: {
22
- color: colorDark,
23
- },
24
-
25
- white: {
26
- color: colorWhite,
27
- },
28
-
29
- gray: {
30
- color: colorDark2,
31
- },
32
-
33
- 'bg-dark': {
34
- backgroundColor: colorDark2,
35
- },
36
-
37
- 'bg-gray': {
38
- backgroundColor: colorGray,
39
- },
40
-
41
- flex: {
42
- display: 'flex',
43
- flexDirection: 'row',
44
- flexWrap: 'nowrap',
45
- alignItems: 'start',
46
- },
47
-
48
- bold: {
49
- fontWeight: 'bold',
50
- },
51
-
52
- 'w-auto': {
53
- flex: 1,
54
- paddingRight: '8px',
55
- },
56
-
57
- 'flex-1': {
58
- flex: 1,
59
- },
60
-
61
- 'w-100': {
62
- width: '100%',
63
- },
64
-
65
- 'w-50': {
66
- width: '50%',
67
- },
68
-
69
- 'w-55': {
70
- width: '55%',
71
- },
72
-
73
- 'w-45': {
74
- width: '45%',
75
- },
76
-
77
- 'w-60': {
78
- width: '60%',
79
- },
80
-
81
- 'w-40': {
82
- width: '30%',
83
- },
84
-
85
- 'w-48': {
86
- width: '48%',
87
- },
88
-
89
- 'w-17': {
90
- width: '17%',
91
- },
92
-
93
- 'w-18': {
94
- width: '18%',
95
- },
96
-
97
- row: {
98
- borderBottom: `1px solid ${colorGray}`,
99
- },
100
-
101
- 'mt-40': {
102
- marginTop: '40px',
103
- },
104
-
105
- 'mt-30': {
106
- marginTop: '30px',
107
- },
108
-
109
- 'mt-20': {
110
- marginTop: '20px',
111
- },
112
-
113
- 'mt-10': {
114
- marginTop: '10px',
115
- },
116
-
117
- 'mb-5': {
118
- marginBottom: '5px',
119
- },
120
-
121
- 'p-4-8': {
122
- padding: '4px 8px',
123
- },
124
-
125
- 'p-5': {
126
- padding: '5px',
127
- },
128
-
129
- 'pb-10': {
130
- paddingBottom: '10px',
131
- },
132
-
133
- right: {
134
- textAlign: 'right',
135
- },
136
- center: {
137
- textAlign: 'right',
138
- },
139
-
140
- 'fs-20': {
141
- fontSize: '20px',
142
- },
143
-
144
- 'fs-30': {
145
- fontSize: '30px',
146
- },
147
-
148
- 'fs-12': {
149
- fontSize: '11px',
150
- },
151
-
152
- page: {
153
- fontFamily: 'Noto Sans SC',
154
- fontSize: '11px',
155
- color: '#555',
156
- padding: '40px 35px',
157
- fontWeight: 'bold',
158
- },
159
-
160
- span: {
161
- padding: '4px 12px 4px 0',
162
- },
163
-
164
- logo: {
165
- display: 'block',
166
- borderRadius: '10px',
167
- width: '60px',
168
- height: '60px',
169
- textAlign: 'center',
170
- },
171
- };
172
-
173
- export default compose;