payment-kit 1.15.23 → 1.15.24
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 +1 -1
- package/index.html +19 -13
- package/package.json +23 -18
- package/src/app.tsx +4 -1
- package/src/components/copyable.tsx +1 -0
- package/src/components/customer/form.tsx +4 -12
- package/src/components/invoice-pdf/pdf.tsx +102 -169
- package/src/components/invoice-pdf/styles.ts +134 -0
- package/src/components/invoice-pdf/template.tsx +127 -0
- package/src/components/invoice-pdf/types.ts +11 -0
- package/src/components/invoice-pdf/utils.ts +41 -0
- package/src/components/pricing-table/payment-settings.tsx +4 -0
- package/src/components/uploader.tsx +32 -26
- package/src/components/webhook/attempts.tsx +3 -0
- package/src/pages/admin/billing/index.tsx +4 -0
- package/src/pages/admin/customers/index.tsx +4 -0
- package/src/pages/admin/developers/index.tsx +10 -1
- package/src/pages/admin/index.tsx +4 -0
- package/src/pages/admin/payments/index.tsx +4 -0
- package/src/pages/admin/products/index.tsx +4 -0
- package/src/pages/admin/products/links/create.tsx +4 -0
- package/src/pages/admin/settings/index.tsx +4 -0
- package/src/pages/customer/index.tsx +1 -1
- package/vite.config.ts +43 -2
- package/src/components/invoice-pdf/compose.ts +0 -173
package/blocklet.yml
CHANGED
package/index.html
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<link rel="icon" href="/favicon.ico?imageFilter=resize&w=32" />
|
|
7
|
+
<link rel="preconnect" href="https://unpkg.com" crossorigin />
|
|
8
|
+
<link rel="preconnect" href="https://js.stripe.com" crossorigin />
|
|
9
|
+
<link rel="dns-prefetch" href="https://js.stripe.com" />
|
|
10
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
|
|
11
|
+
<meta name="theme-color" content="#4F6AF5" />
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<noscript>You need to enable JavaScript to run this app. </noscript>
|
|
16
|
+
<div id="app"></div>
|
|
17
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
18
|
+
</body>
|
|
19
|
+
|
|
20
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.24",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"clean": "node scripts/build-clean.js",
|
|
14
14
|
"bundle": "tsc --noEmit && npm run bundle:client && npm run bundle:api",
|
|
15
15
|
"bundle:client": "vite build",
|
|
16
|
+
"bundle:analyze": "cross-env ANALYZE=true vite build",
|
|
16
17
|
"bundle:api": "npm run clean && tsc -p tsconfig.api.json && blocklet bundle --compact --monorepo --create-release --changelog",
|
|
17
18
|
"build": "npm run clean && tsc -p tsconfig.api.json && npm run bundle:client",
|
|
18
19
|
"types": "rm -rf types && tsc -p tsconfig.types.json && rm -f ../../packages/types/lib/*.d.ts && cp -f types/store/models/*.d.ts ../../packages/types/lib",
|
|
@@ -43,30 +44,29 @@
|
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@abtnode/cron": "^1.16.32",
|
|
46
|
-
"@arcblock/did": "^1.18.
|
|
47
|
+
"@arcblock/did": "^1.18.137",
|
|
47
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
48
|
-
"@arcblock/did-connect": "^2.10.
|
|
49
|
-
"@arcblock/did-util": "^1.18.
|
|
50
|
-
"@arcblock/jwt": "^1.18.
|
|
51
|
-
"@arcblock/ux": "^2.10.
|
|
52
|
-
"@arcblock/validator": "^1.18.
|
|
49
|
+
"@arcblock/did-connect": "^2.10.55",
|
|
50
|
+
"@arcblock/did-util": "^1.18.137",
|
|
51
|
+
"@arcblock/jwt": "^1.18.137",
|
|
52
|
+
"@arcblock/ux": "^2.10.55",
|
|
53
|
+
"@arcblock/validator": "^1.18.137",
|
|
53
54
|
"@blocklet/js-sdk": "^1.16.32",
|
|
54
55
|
"@blocklet/logger": "^1.16.32",
|
|
55
|
-
"@blocklet/payment-react": "1.15.
|
|
56
|
+
"@blocklet/payment-react": "1.15.24",
|
|
56
57
|
"@blocklet/sdk": "^1.16.32",
|
|
57
|
-
"@blocklet/ui-react": "^2.10.
|
|
58
|
-
"@blocklet/uploader": "^0.1.
|
|
58
|
+
"@blocklet/ui-react": "^2.10.55",
|
|
59
|
+
"@blocklet/uploader": "^0.1.51",
|
|
59
60
|
"@blocklet/xss": "^0.1.12",
|
|
60
61
|
"@mui/icons-material": "^5.16.6",
|
|
61
62
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
62
63
|
"@mui/material": "^5.16.6",
|
|
63
64
|
"@mui/system": "^5.16.6",
|
|
64
|
-
"@ocap/asset": "^1.18.
|
|
65
|
-
"@ocap/client": "^1.18.
|
|
66
|
-
"@ocap/mcrypto": "^1.18.
|
|
67
|
-
"@ocap/util": "^1.18.
|
|
68
|
-
"@ocap/wallet": "^1.18.
|
|
69
|
-
"@react-pdf/renderer": "^3.4.4",
|
|
65
|
+
"@ocap/asset": "^1.18.137",
|
|
66
|
+
"@ocap/client": "^1.18.137",
|
|
67
|
+
"@ocap/mcrypto": "^1.18.137",
|
|
68
|
+
"@ocap/util": "^1.18.137",
|
|
69
|
+
"@ocap/wallet": "^1.18.137",
|
|
70
70
|
"@stripe/react-stripe-js": "^2.7.3",
|
|
71
71
|
"@stripe/stripe-js": "^2.4.0",
|
|
72
72
|
"ahooks": "^3.8.0",
|
|
@@ -87,9 +87,11 @@
|
|
|
87
87
|
"fastq": "^1.17.1",
|
|
88
88
|
"flat": "^5.0.2",
|
|
89
89
|
"google-libphonenumber": "^3.2.38",
|
|
90
|
+
"html2canvas": "^1.4.1",
|
|
90
91
|
"iframe-resizer-react": "^1.1.1",
|
|
91
92
|
"joi": "^17.13.3",
|
|
92
93
|
"json-stable-stringify": "^1.1.1",
|
|
94
|
+
"jspdf": "^2.5.2",
|
|
93
95
|
"lodash": "^4.17.21",
|
|
94
96
|
"morgan": "^1.10.0",
|
|
95
97
|
"mui-daterange-picker": "^1.0.5",
|
|
@@ -118,7 +120,7 @@
|
|
|
118
120
|
"devDependencies": {
|
|
119
121
|
"@abtnode/types": "^1.16.32",
|
|
120
122
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
121
|
-
"@blocklet/payment-types": "1.15.
|
|
123
|
+
"@blocklet/payment-types": "1.15.24",
|
|
122
124
|
"@types/cookie-parser": "^1.4.7",
|
|
123
125
|
"@types/cors": "^2.8.17",
|
|
124
126
|
"@types/debug": "^4.1.12",
|
|
@@ -128,6 +130,7 @@
|
|
|
128
130
|
"@types/react": "^18.3.3",
|
|
129
131
|
"@types/react-dom": "^18.3.0",
|
|
130
132
|
"@vitejs/plugin-react": "^4.3.1",
|
|
133
|
+
"babel-plugin-lodash": "^3.3.4",
|
|
131
134
|
"bumpp": "^8.2.1",
|
|
132
135
|
"cross-env": "^7.0.3",
|
|
133
136
|
"eslint": "^8.57.0",
|
|
@@ -138,12 +141,14 @@
|
|
|
138
141
|
"npm-run-all": "^4.1.5",
|
|
139
142
|
"prettier": "^3.3.3",
|
|
140
143
|
"prettier-plugin-import-sort": "^0.0.7",
|
|
144
|
+
"rollup-plugin-visualizer": "^5.12.0",
|
|
141
145
|
"ts-jest": "^29.2.5",
|
|
142
146
|
"ts-node": "^10.9.2",
|
|
143
147
|
"type-fest": "^4.23.0",
|
|
144
148
|
"typescript": "^4.9.5",
|
|
145
149
|
"vite": "^5.3.5",
|
|
146
150
|
"vite-node": "^2.0.4",
|
|
151
|
+
"vite-plugin-babel-import": "^2.0.5",
|
|
147
152
|
"vite-plugin-blocklet": "^0.9.11",
|
|
148
153
|
"vite-plugin-node-polyfills": "^0.21.0",
|
|
149
154
|
"vite-plugin-svgr": "^4.2.0",
|
|
@@ -160,5 +165,5 @@
|
|
|
160
165
|
"parser": "typescript"
|
|
161
166
|
}
|
|
162
167
|
},
|
|
163
|
-
"gitHead": "
|
|
168
|
+
"gitHead": "8139f401d570847d397f27caeea4690d550b21b4"
|
|
164
169
|
}
|
package/src/app.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import './global.css';
|
|
2
2
|
|
|
3
3
|
import Center from '@arcblock/ux/lib/Center';
|
|
4
|
+
import withTracker from '@arcblock/ux/lib/withTracker';
|
|
4
5
|
import { LocaleProvider } from '@arcblock/ux/lib/Locale/context';
|
|
5
6
|
// import { createTheme } from '@arcblock/ux/lib/Theme';
|
|
6
7
|
import { ToastProvider } from '@arcblock/ux/lib/Toast';
|
|
@@ -143,6 +144,8 @@ function App() {
|
|
|
143
144
|
);
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
const AppWithTracker = withTracker(App);
|
|
148
|
+
|
|
146
149
|
export default function WrappedApp() {
|
|
147
150
|
// While the blocklet is deploy to a sub path, this will be work properly.
|
|
148
151
|
const prefix = window?.blocklet?.prefix || '/';
|
|
@@ -153,7 +156,7 @@ export default function WrappedApp() {
|
|
|
153
156
|
serviceHost={prefix}
|
|
154
157
|
protectedRoutes={['/admin/*', '/customer/*'].map((item) => joinURL(prefix, item))}>
|
|
155
158
|
<Router basename={prefix}>
|
|
156
|
-
<
|
|
159
|
+
<AppWithTracker />
|
|
157
160
|
</Router>
|
|
158
161
|
</SessionProvider>
|
|
159
162
|
</ToastProvider>
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import 'react-international-phone/style.css';
|
|
2
2
|
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
|
-
import { FormInput, PhoneInput, CountrySelect } from '@blocklet/payment-react';
|
|
4
|
+
import { FormInput, PhoneInput, CountrySelect, validatePhoneNumber } from '@blocklet/payment-react';
|
|
5
5
|
import { FormLabel, Stack } from '@mui/material';
|
|
6
|
-
import { PhoneNumberUtil } from 'google-libphonenumber';
|
|
7
6
|
import { Controller, useFormContext } from 'react-hook-form';
|
|
8
7
|
import isEmail from 'validator/es/lib/isEmail';
|
|
9
8
|
|
|
10
|
-
const phoneUtil = PhoneNumberUtil.getInstance();
|
|
11
|
-
|
|
12
9
|
export default function CustomerForm() {
|
|
13
10
|
const { t } = useLocaleContext();
|
|
14
11
|
const { control } = useFormContext();
|
|
@@ -63,14 +60,9 @@ export default function CustomerForm() {
|
|
|
63
60
|
placeholder={t('payment.checkout.customer.phonePlaceholder')}
|
|
64
61
|
rules={{
|
|
65
62
|
required: t('payment.checkout.required'),
|
|
66
|
-
validate: (x: string) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return phoneUtil.isValidNumber(parsed) ? true : t('payment.checkout.invalid');
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.error(err, x);
|
|
72
|
-
return t('payment.checkout.invalid');
|
|
73
|
-
}
|
|
63
|
+
validate: async (x: string) => {
|
|
64
|
+
const isValid = await validatePhoneNumber(x);
|
|
65
|
+
return isValid ? true : t('payment.checkout.invalid');
|
|
74
66
|
},
|
|
75
67
|
}}
|
|
76
68
|
/>
|
|
@@ -1,185 +1,118 @@
|
|
|
1
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
1
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
-
import { formatTime, getPrefix } from '@blocklet/payment-react';
|
|
3
3
|
import type { TInvoiceExpanded } from '@blocklet/payment-types';
|
|
4
|
-
import { Button } from '@mui/material';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
PDFDownloadLink,
|
|
8
|
-
Document as PdfDocument,
|
|
9
|
-
Image as PdfImage,
|
|
10
|
-
Page as PdfPage,
|
|
11
|
-
Text as PdfText,
|
|
12
|
-
View as PdfView,
|
|
13
|
-
} from '@react-pdf/renderer';
|
|
14
|
-
import { joinURL } from 'ufo';
|
|
4
|
+
import { Box, Button, CircularProgress } from '@mui/material';
|
|
5
|
+
import JsPDF from 'jspdf';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
15
7
|
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
8
|
+
import { loadFont, loadImage } from './utils';
|
|
9
|
+
import { InvoiceTemplate } from './template';
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
export function Download({ data }: { data: TInvoiceExpanded }) {
|
|
12
|
+
const { t } = useLocaleContext();
|
|
13
|
+
const [isReady, setIsReady] = useState(false);
|
|
14
|
+
const [isGenerating, setIsGenerating] = useState(false);
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const preload = async () => {
|
|
18
|
+
try {
|
|
19
|
+
const [fontLoaded] = await Promise.all([loadFont(), loadImage(window.blocklet.appLogo?.split('?')[0] || '')]);
|
|
20
|
+
setIsReady(fontLoaded);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error('Resource loading failed:', error);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
preload();
|
|
26
|
+
}, []);
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
28
|
+
const generatePDF = async () => {
|
|
29
|
+
if (!isReady || isGenerating) return;
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
31
|
+
try {
|
|
32
|
+
setIsGenerating(true);
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const element = document.getElementById('invoice-template');
|
|
35
|
+
if (!element) return;
|
|
36
|
+
const html2canvas = (await import(/* webpackChunkName: "html2canvas" */ 'html2canvas')).default;
|
|
37
|
+
const canvas = await html2canvas(element, {
|
|
38
|
+
scale: 5,
|
|
39
|
+
useCORS: true,
|
|
40
|
+
logging: false,
|
|
41
|
+
windowWidth: 794,
|
|
42
|
+
windowHeight: 1123,
|
|
43
|
+
allowTaint: true,
|
|
44
|
+
backgroundColor: '#ffffff',
|
|
45
|
+
imageTimeout: 15000,
|
|
46
|
+
removeContainer: true,
|
|
47
|
+
onclone: (clonedDoc) => {
|
|
48
|
+
const clonedElement = clonedDoc.getElementById('invoice-template');
|
|
49
|
+
if (clonedElement) {
|
|
50
|
+
clonedElement.style.position = 'relative';
|
|
51
|
+
clonedElement.style.left = '0';
|
|
52
|
+
clonedElement.style.transform = 'scale(1)';
|
|
53
|
+
clonedElement.style.transformOrigin = 'top left';
|
|
54
|
+
clonedElement.style.textRendering = 'geometricPrecision';
|
|
55
|
+
(clonedElement.style as any)['-webkit-font-smoothing'] = 'antialiased';
|
|
56
|
+
(clonedElement.style as any)['-moz-osx-font-smoothing'] = 'grayscale';
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
});
|
|
39
60
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
const pdf = new JsPDF({
|
|
62
|
+
orientation: 'portrait',
|
|
63
|
+
unit: 'pt',
|
|
64
|
+
format: 'a4',
|
|
65
|
+
compress: true,
|
|
66
|
+
precision: 32,
|
|
67
|
+
hotfixes: ['px_scaling'],
|
|
68
|
+
});
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<PDFDownloadLink
|
|
54
|
-
key="pdf"
|
|
55
|
-
document={<InvoicePage data={data} t={t} />}
|
|
56
|
-
fileName={`${title}.pdf`}
|
|
57
|
-
aria-label="Save PDF"
|
|
58
|
-
title="Save PDF"
|
|
59
|
-
className="download-pdf__pdf">
|
|
60
|
-
<Button
|
|
61
|
-
variant="outlined"
|
|
62
|
-
color="primary"
|
|
63
|
-
size="small"
|
|
64
|
-
sx={{ borderColor: 'var(--stroke-border-base, #EFF1F5)' }}>
|
|
65
|
-
{t('payment.customer.invoice.download')}
|
|
66
|
-
</Button>
|
|
67
|
-
</PDFDownloadLink>
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function InvoicePage({ data, t }: { data: TInvoiceExpanded; t: any }) {
|
|
73
|
-
const { detail, summary } = getInvoiceRows(data);
|
|
70
|
+
const imgWidth = canvas.width;
|
|
71
|
+
const imgHeight = canvas.height;
|
|
72
|
+
const pdfWidth = pdf.internal.pageSize.getWidth();
|
|
73
|
+
const pdfHeight = pdf.internal.pageSize.getHeight();
|
|
74
|
+
const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
...compose('image logo center'),
|
|
86
|
-
}}
|
|
87
|
-
src={window.blocklet.appLogo.split('?')[0]}
|
|
88
|
-
/>
|
|
89
|
-
<Text2 value={window.blocklet.appName} className="center p-10" />
|
|
90
|
-
</View>
|
|
91
|
-
</View>
|
|
92
|
-
|
|
93
|
-
<View className="flex mt-40">
|
|
94
|
-
<View className="w-60">
|
|
95
|
-
<Text2 className="bold dark mb-5" value={t('admin.invoice.billTo')} />
|
|
96
|
-
<Text2 value={data.customer.name} />
|
|
97
|
-
<Text2 className="bold fs-12" value={`DID:ABT:${data.customer.did}`} />
|
|
98
|
-
</View>
|
|
99
|
-
<View className="w-45">
|
|
100
|
-
<View className="flex mb-5">
|
|
101
|
-
<View className="w-45">
|
|
102
|
-
<Text2 className="bold" value={t('admin.invoice.number')} />
|
|
103
|
-
</View>
|
|
104
|
-
<View className="w-60">
|
|
105
|
-
<Text2 value={data.number} className="gray" />
|
|
106
|
-
</View>
|
|
107
|
-
</View>
|
|
108
|
-
<View className="flex mb-5">
|
|
109
|
-
<View className="w-45">
|
|
110
|
-
<Text2 className="bold" value={t('admin.invoice.paidAt')} />
|
|
111
|
-
</View>
|
|
112
|
-
<View className="w-60">
|
|
113
|
-
<Text2 value={formatTime(data.period_start * 1000)} className="gray" />
|
|
114
|
-
</View>
|
|
115
|
-
</View>
|
|
116
|
-
<View className="flex mb-5">
|
|
117
|
-
<View className="w-45">
|
|
118
|
-
<Text2 className="bold" value={t('admin.invoice.dueDate')} />
|
|
119
|
-
</View>
|
|
120
|
-
<View className="w-60">
|
|
121
|
-
<Text2 value={formatTime(data.period_end * 1000)} className="gray" />
|
|
122
|
-
</View>
|
|
123
|
-
</View>
|
|
124
|
-
</View>
|
|
125
|
-
</View>
|
|
126
|
-
|
|
127
|
-
<View className="mt-30 row flex">
|
|
128
|
-
<View className="w-48 p-4-8">
|
|
129
|
-
<Text2 className="bold" value={t('admin.subscription.product')} />
|
|
130
|
-
</View>
|
|
131
|
-
<View className="w-17 p-4-8">
|
|
132
|
-
<Text2 className="bold right" value={t('common.quantity')} />
|
|
133
|
-
</View>
|
|
134
|
-
<View className="w-17 p-4-8">
|
|
135
|
-
<Text2 className="bold right" value={t('payment.customer.invoice.unitPrice')} />
|
|
136
|
-
</View>
|
|
137
|
-
<View className="w-18 p-4-8">
|
|
138
|
-
<Text2 className="bold right" value={t('common.amount')} />
|
|
139
|
-
</View>
|
|
140
|
-
</View>
|
|
76
|
+
pdf.addImage(
|
|
77
|
+
canvas.toDataURL('image/png', 1.0),
|
|
78
|
+
'PNG',
|
|
79
|
+
0,
|
|
80
|
+
0,
|
|
81
|
+
imgWidth * ratio,
|
|
82
|
+
imgHeight * ratio,
|
|
83
|
+
undefined,
|
|
84
|
+
'FAST'
|
|
85
|
+
);
|
|
141
86
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
<Text2 className="dark right" value={line.quantity} />
|
|
150
|
-
</View>
|
|
151
|
-
<View className="w-17 p-4-8 pb-10">
|
|
152
|
-
<Text className="dark right">{line.price ? `${line.price} ${data.paymentCurrency.symbol}` : ''}</Text>
|
|
153
|
-
</View>
|
|
154
|
-
<View className="w-18 p-4-8 pb-10">
|
|
155
|
-
<Text className="dark right">
|
|
156
|
-
{line.amount} {data.paymentCurrency.symbol}
|
|
157
|
-
</Text>
|
|
158
|
-
</View>
|
|
159
|
-
</View>
|
|
160
|
-
);
|
|
161
|
-
})}
|
|
87
|
+
pdf.save(`${data.number}.pdf`);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('PDF generation failed:', error);
|
|
90
|
+
} finally {
|
|
91
|
+
setIsGenerating(false);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
162
94
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
</
|
|
181
|
-
|
|
95
|
+
return (
|
|
96
|
+
<div className="download-pdf">
|
|
97
|
+
<Button
|
|
98
|
+
variant="outlined"
|
|
99
|
+
color="primary"
|
|
100
|
+
size="small"
|
|
101
|
+
onClick={generatePDF}
|
|
102
|
+
disabled={!isReady || isGenerating}
|
|
103
|
+
sx={{ borderColor: 'var(--stroke-border-base, #EFF1F5)' }}>
|
|
104
|
+
{isGenerating ? (
|
|
105
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
106
|
+
<CircularProgress size={16} sx={{ color: 'inherit' }} />
|
|
107
|
+
{t('payment.customer.invoice.download')}
|
|
108
|
+
</Box>
|
|
109
|
+
) : (
|
|
110
|
+
t('payment.customer.invoice.download')
|
|
111
|
+
)}
|
|
112
|
+
</Button>
|
|
113
|
+
<InvoiceTemplate data={data} t={t} />
|
|
114
|
+
</div>
|
|
182
115
|
);
|
|
183
116
|
}
|
|
184
117
|
|
|
185
|
-
export default
|
|
118
|
+
export default Download;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { InvoiceStyles } from './types';
|
|
2
|
+
|
|
3
|
+
export const colorDark = '#222';
|
|
4
|
+
export const colorDark2 = '#888';
|
|
5
|
+
export const colorGray = '#e3e3e3';
|
|
6
|
+
export const colorWhite = '#fff';
|
|
7
|
+
|
|
8
|
+
export const pdfStyles: InvoiceStyles = {
|
|
9
|
+
template: {
|
|
10
|
+
padding: '40px 35px',
|
|
11
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
12
|
+
fontSize: '11px',
|
|
13
|
+
color: '#555',
|
|
14
|
+
backgroundColor: '#fff',
|
|
15
|
+
width: '210mm',
|
|
16
|
+
minHeight: '297mm',
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
left: '-9999px',
|
|
19
|
+
printColorAdjust: 'exact',
|
|
20
|
+
letterSpacing: '0.3px',
|
|
21
|
+
lineHeight: '1.5',
|
|
22
|
+
},
|
|
23
|
+
dark: {
|
|
24
|
+
color: colorDark,
|
|
25
|
+
},
|
|
26
|
+
white: {
|
|
27
|
+
color: colorWhite,
|
|
28
|
+
},
|
|
29
|
+
gray: {
|
|
30
|
+
color: colorDark2,
|
|
31
|
+
},
|
|
32
|
+
'bg-dark': {
|
|
33
|
+
backgroundColor: colorDark2,
|
|
34
|
+
},
|
|
35
|
+
'bg-gray': {
|
|
36
|
+
backgroundColor: colorGray,
|
|
37
|
+
},
|
|
38
|
+
flex: {
|
|
39
|
+
display: 'flex',
|
|
40
|
+
flexDirection: 'row',
|
|
41
|
+
flexWrap: 'nowrap',
|
|
42
|
+
alignItems: 'flex-start',
|
|
43
|
+
},
|
|
44
|
+
bold: {
|
|
45
|
+
fontWeight: 'bold',
|
|
46
|
+
},
|
|
47
|
+
'w-auto': {
|
|
48
|
+
flex: 1,
|
|
49
|
+
paddingRight: '8px',
|
|
50
|
+
},
|
|
51
|
+
'w-100': { width: '100%' },
|
|
52
|
+
'w-50': { width: '50%' },
|
|
53
|
+
'w-55': { width: '55%' },
|
|
54
|
+
'w-45': { width: '45%' },
|
|
55
|
+
'w-60': { width: '60%' },
|
|
56
|
+
'w-40': { width: '40%' },
|
|
57
|
+
'w-48': { width: '48%' },
|
|
58
|
+
'w-17': { width: '17%' },
|
|
59
|
+
'w-18': { width: '18%' },
|
|
60
|
+
row: {
|
|
61
|
+
borderBottom: `1px solid ${colorGray}`,
|
|
62
|
+
marginBottom: '2px',
|
|
63
|
+
},
|
|
64
|
+
'mt-60': { marginTop: '60px' },
|
|
65
|
+
'mt-40': { marginTop: '40px' },
|
|
66
|
+
'mt-30': { marginTop: '30px' },
|
|
67
|
+
'mt-20': { marginTop: '20px' },
|
|
68
|
+
'mt-10': { marginTop: '10px' },
|
|
69
|
+
'mb-5': { marginBottom: '5px' },
|
|
70
|
+
'p-4-8': { padding: '4px 8px' },
|
|
71
|
+
'p-5': { padding: '5px' },
|
|
72
|
+
'p-10': { padding: '10px' },
|
|
73
|
+
'pb-15': { paddingBottom: '15px' },
|
|
74
|
+
right: {
|
|
75
|
+
marginLeft: 'auto',
|
|
76
|
+
textAlign: 'right' as const,
|
|
77
|
+
},
|
|
78
|
+
center: { textAlign: 'center' },
|
|
79
|
+
'fs-20': { fontSize: '20px' },
|
|
80
|
+
'fs-30': { fontSize: '30px' },
|
|
81
|
+
'fs-12': { fontSize: '12px' },
|
|
82
|
+
logo: {
|
|
83
|
+
display: 'block',
|
|
84
|
+
borderRadius: '10px',
|
|
85
|
+
width: '60px',
|
|
86
|
+
height: '60px',
|
|
87
|
+
objectFit: 'contain',
|
|
88
|
+
},
|
|
89
|
+
'flex-1': {
|
|
90
|
+
flex: 1,
|
|
91
|
+
},
|
|
92
|
+
'justify-between': {
|
|
93
|
+
justifyContent: 'space-between',
|
|
94
|
+
},
|
|
95
|
+
'items-center': {
|
|
96
|
+
alignItems: 'center',
|
|
97
|
+
},
|
|
98
|
+
'gap-20': {
|
|
99
|
+
gap: '20px',
|
|
100
|
+
},
|
|
101
|
+
block: { display: 'block' },
|
|
102
|
+
'ml-40': { marginLeft: '40px' },
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const styles = `
|
|
106
|
+
.invoice-header {
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: flex-end;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 20px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.invoice-content {
|
|
114
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
115
|
+
letter-spacing: 0.3px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.date-format {
|
|
119
|
+
font-family: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Monaco, Consolas, monospace;
|
|
120
|
+
letter-spacing: 0.5px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
* {
|
|
124
|
+
-webkit-font-smoothing: antialiased;
|
|
125
|
+
-moz-osx-font-smoothing: grayscale;
|
|
126
|
+
text-rendering: optimizeLegibility;
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
export const applyStyles = () => {
|
|
131
|
+
const styleElement = document.createElement('style');
|
|
132
|
+
styleElement.textContent = styles;
|
|
133
|
+
document.head.appendChild(styleElement);
|
|
134
|
+
};
|