payment-kit 1.18.3 → 1.18.4

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,229 @@
1
+ import { useState } from 'react';
2
+ import { Box, Typography, Button, TextField, Stack, Paper, Divider } from '@mui/material';
3
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
+ import type { TSetting } from '@blocklet/payment-types';
5
+
6
+ export default function DonationPreview({ config }: { config: TSetting['settings'] }) {
7
+ const { t } = useLocaleContext();
8
+ const hasPresets = config.settings.amount.presets?.length > 0;
9
+ const [selectedAmount, setSelectedAmount] = useState(config.settings.amount.preset);
10
+ const [customAmount, setCustomAmount] = useState('');
11
+ const [showCustom, setShowCustom] = useState(hasPresets ? false : config.settings.amount.custom);
12
+
13
+ const handleAmountSelect = (amount: string) => {
14
+ setSelectedAmount(amount);
15
+ setShowCustom(false);
16
+ setCustomAmount('');
17
+ };
18
+
19
+ const handleCustomClick = () => {
20
+ setShowCustom(true);
21
+ setSelectedAmount('');
22
+ };
23
+
24
+ return (
25
+ <Stack spacing={3}>
26
+ <Typography variant="h6" gutterBottom color="text.primary">
27
+ {t('admin.donate.previewTitle')}
28
+ </Typography>
29
+
30
+ {/* Button Preview */}
31
+ <Paper
32
+ elevation={0}
33
+ sx={{
34
+ p: 2,
35
+ bgcolor: 'background.paper',
36
+ border: '1px dashed',
37
+ borderColor: 'divider',
38
+ borderRadius: 1,
39
+ }}>
40
+ <Typography variant="caption" display="block" gutterBottom color="text.secondary" mb={1}>
41
+ {t('admin.donate.btn.preview')}
42
+ </Typography>
43
+ <Stack spacing={2} alignItems="center">
44
+ <Button variant="contained" color="primary">
45
+ {config.settings.btnText}
46
+ </Button>
47
+
48
+ <Box sx={{ width: '100%', maxWidth: 300 }}>
49
+ <Typography variant="caption" display="block" gutterBottom color="text.secondary" mb={1} textAlign="center">
50
+ total XX ABT
51
+ </Typography>
52
+ {config.settings.historyType === 'avatar' ? (
53
+ <Box
54
+ sx={{
55
+ height: 40,
56
+ position: 'relative',
57
+ display: 'flex',
58
+ alignItems: 'center',
59
+ justifyContent: 'center',
60
+ }}>
61
+ {Array(5)
62
+ .fill(0)
63
+ .map((_, i) => (
64
+ <Box
65
+ // eslint-disable-next-line react/no-array-index-key
66
+ key={i}
67
+ sx={{
68
+ width: 32,
69
+ height: 32,
70
+ borderRadius: '50%',
71
+ bgcolor: '#e0e0e0',
72
+ position: 'absolute',
73
+ left: `calc(50% - 16px + ${i * 24}px - ${2 * 24}px)`,
74
+ opacity: 1 - i * 0.1,
75
+ border: '2px solid #fff',
76
+ }}
77
+ />
78
+ ))}
79
+ </Box>
80
+ ) : (
81
+ <Box
82
+ sx={{
83
+ borderRadius: 1,
84
+ p: 1.5,
85
+ display: 'flex',
86
+ flexDirection: 'column',
87
+ alignItems: 'center',
88
+ gap: 1.5,
89
+ }}>
90
+ {Array(3)
91
+ .fill(0)
92
+ .map((_, i) => (
93
+ <Box
94
+ // eslint-disable-next-line react/no-array-index-key
95
+ key={i}
96
+ sx={{
97
+ display: 'flex',
98
+ justifyContent: 'center',
99
+ gap: 2,
100
+ opacity: 1 - i * 0.2,
101
+ width: '100%',
102
+ maxWidth: 240,
103
+ }}>
104
+ <Box
105
+ sx={{
106
+ width: '30%',
107
+ height: 8,
108
+ borderRadius: 1,
109
+ bgcolor: '#e0e0e0',
110
+ }}
111
+ />
112
+ <Box
113
+ sx={{
114
+ width: '20%',
115
+ height: 8,
116
+ borderRadius: 1,
117
+ bgcolor: '#e0e0e0',
118
+ }}
119
+ />
120
+ <Box
121
+ sx={{
122
+ width: '15%',
123
+ height: 8,
124
+ borderRadius: 1,
125
+ bgcolor: '#e0e0e0',
126
+ }}
127
+ />
128
+ </Box>
129
+ ))}
130
+ </Box>
131
+ )}
132
+ </Box>
133
+ </Stack>
134
+ </Paper>
135
+
136
+ {/* Dialog Preview */}
137
+ <Paper
138
+ elevation={0}
139
+ sx={{
140
+ p: 2,
141
+ bgcolor: 'background.paper',
142
+ border: '1px dashed',
143
+ borderColor: 'divider',
144
+ borderRadius: 1,
145
+ }}>
146
+ <Typography variant="caption" display="block" gutterBottom color="text.secondary" mb={1}>
147
+ {t('admin.donate.dialog.preview')}
148
+ </Typography>
149
+ <Stack spacing={3}>
150
+ <Box display="flex" gap={1} flexWrap="wrap">
151
+ {config.settings.amount.presets?.map((amount: string) => (
152
+ <Button
153
+ key={amount}
154
+ variant="outlined"
155
+ size="small"
156
+ sx={{
157
+ borderColor: selectedAmount === amount ? 'primary.main' : 'divider',
158
+ borderWidth: selectedAmount === amount ? 2 : 1,
159
+ color: selectedAmount === amount ? 'primary.main' : 'text.primary',
160
+ }}
161
+ onClick={() => handleAmountSelect(amount)}>
162
+ {`${amount} ABT`}
163
+ </Button>
164
+ ))}
165
+ {config.settings.amount.custom && hasPresets && (
166
+ <Button
167
+ variant="outlined"
168
+ size="small"
169
+ sx={{
170
+ borderColor: showCustom ? 'primary.main' : 'divider',
171
+ borderWidth: showCustom ? 2 : 1,
172
+ color: showCustom ? 'primary.main' : 'text.primary',
173
+ }}
174
+ onClick={handleCustomClick}>
175
+ {t('common.custom')}
176
+ </Button>
177
+ )}
178
+ </Box>
179
+
180
+ {showCustom && config.settings.amount.custom && (
181
+ <TextField
182
+ fullWidth
183
+ size="small"
184
+ autoFocus
185
+ label={t('admin.donate.customAmount')}
186
+ value={customAmount}
187
+ onChange={(e) => {
188
+ const { value } = e.target;
189
+ setCustomAmount(value);
190
+ }}
191
+ InputProps={{
192
+ endAdornment: <Typography sx={{ ml: 1 }}>ABT</Typography>,
193
+ autoComplete: 'off',
194
+ }}
195
+ helperText={t('admin.donate.amountRange', {
196
+ min: config.settings.amount.minimum,
197
+ max: config.settings.amount.maximum,
198
+ })}
199
+ />
200
+ )}
201
+ </Stack>
202
+ <Divider sx={{ my: 2 }} />
203
+ <Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
204
+ <Box
205
+ sx={{
206
+ px: 2,
207
+ py: 0.5,
208
+ bgcolor: '#f5f5f5',
209
+ borderRadius: 1,
210
+ width: 40,
211
+ height: 24,
212
+ }}
213
+ />
214
+ <Box
215
+ sx={{
216
+ px: 2,
217
+ py: 0.5,
218
+ bgcolor: 'primary.main',
219
+ borderRadius: 1,
220
+ width: 40,
221
+ height: 24,
222
+ opacity: 0.5,
223
+ }}
224
+ />
225
+ </Box>
226
+ </Paper>
227
+ </Stack>
228
+ );
229
+ }
@@ -0,0 +1,80 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { Stack } from '@mui/material';
3
+ import React, { useEffect } from 'react';
4
+ import { useNavigate, useParams } from 'react-router-dom';
5
+ import Tabs from '@arcblock/ux/lib/Tabs';
6
+
7
+ import Layout from '../../components/layout/admin';
8
+ import ProgressBar, { useTransitionContext } from '../../components/progress-bar';
9
+ import { useSessionContext } from '../../contexts/session';
10
+
11
+ const pages = {
12
+ overview: React.lazy(() => import('./overview')),
13
+ donations: React.lazy(() => import('./donations')),
14
+ };
15
+
16
+ function Integrations() {
17
+ const navigate = useNavigate();
18
+ const { t } = useLocaleContext();
19
+ const { group = 'overview' } = useParams();
20
+ const { isPending, startTransition } = useTransitionContext();
21
+
22
+ const onTabChange = (newTab: string) => {
23
+ startTransition(() => {
24
+ navigate(`/integrations/${newTab}`);
25
+ });
26
+ };
27
+
28
+ // @ts-ignore
29
+ const TabComponent = pages[group] || pages.overview;
30
+ const tabs = [
31
+ { label: t('common.quickStarts'), value: 'overview' },
32
+ { label: t('admin.donate.title'), value: 'donations' },
33
+ ];
34
+
35
+ return (
36
+ <>
37
+ <ProgressBar pending={isPending} />
38
+ <Stack direction="row" alignItems="center" justifyContent="end" flexWrap="wrap" spacing={1} sx={{ mt: 1, pb: 2 }}>
39
+ <Tabs
40
+ tabs={tabs}
41
+ current={group}
42
+ onChange={onTabChange}
43
+ scrollButtons="auto"
44
+ variant="scrollable"
45
+ sx={{
46
+ flex: '1 0 auto',
47
+ maxWidth: '100%',
48
+ '.MuiTab-root': {
49
+ marginBottom: '12px',
50
+ fontWeight: '500',
51
+ color: 'text.lighter',
52
+ '&.Mui-selected': {
53
+ color: 'text.primary',
54
+ },
55
+ },
56
+ '.MuiTouchRipple-root': {
57
+ display: 'none',
58
+ },
59
+ }}
60
+ />
61
+ </Stack>
62
+ <div className="page-content">{React.isValidElement(TabComponent) ? TabComponent : <TabComponent />}</div>
63
+ </>
64
+ );
65
+ }
66
+
67
+ export default function WrappedIntegrations() {
68
+ const { session } = useSessionContext();
69
+ const navigate = useNavigate();
70
+ useEffect(() => {
71
+ if (session.user && ['owner', 'admin'].includes(session.user.role) === false) {
72
+ navigate('/customer');
73
+ }
74
+ }, [session.user]);
75
+ return (
76
+ <Layout>
77
+ <Integrations />
78
+ </Layout>
79
+ );
80
+ }
@@ -0,0 +1,121 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { Box, Card, CardActionArea, CardContent, Grid, Typography, Divider } from '@mui/material';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { Favorite, Inventory, Link as LinkIcon, TableChart, Payment, Code } from '@mui/icons-material';
5
+
6
+ const basicFeatures = [
7
+ {
8
+ title: 'integrations.features.products.title',
9
+ description: 'integrations.features.products.intro',
10
+ icon: <Inventory color="primary" sx={{ fontSize: 40 }} />,
11
+ path: '/admin/products',
12
+ },
13
+ {
14
+ title: 'integrations.features.paymentLinks.title',
15
+ description: 'integrations.features.paymentLinks.intro',
16
+ icon: <LinkIcon color="primary" sx={{ fontSize: 40 }} />,
17
+ path: '/admin/products/links',
18
+ },
19
+ {
20
+ title: 'integrations.features.pricingTables.title',
21
+ description: 'integrations.features.pricingTables.intro',
22
+ icon: <TableChart color="primary" sx={{ fontSize: 40 }} />,
23
+ path: '/admin/products/pricing-tables',
24
+ },
25
+ ];
26
+
27
+ const advancedFeatures = [
28
+ {
29
+ title: 'integrations.features.donate.title',
30
+ description: 'integrations.features.donate.intro',
31
+ icon: <Favorite color="primary" sx={{ fontSize: 40 }} />,
32
+ path: '/integrations/donations',
33
+ },
34
+ {
35
+ title: 'integrations.features.paymentMethods.title',
36
+ description: 'integrations.features.paymentMethods.intro',
37
+ icon: <Payment color="primary" sx={{ fontSize: 40 }} />,
38
+ path: '/admin/settings/payment-methods',
39
+ },
40
+ {
41
+ title: 'integrations.features.api.title',
42
+ description: 'integrations.features.api.intro',
43
+ icon: <Code color="primary" sx={{ fontSize: 40 }} />,
44
+ path: 'https://www.npmjs.com/package/@blocklet/payment-js',
45
+ external: true,
46
+ },
47
+ ];
48
+
49
+ export default function Overview() {
50
+ const { t } = useLocaleContext();
51
+ const navigate = useNavigate();
52
+
53
+ const handleClick = (item: any) => {
54
+ if (item.external) {
55
+ window.open(item.path, '_blank');
56
+ } else {
57
+ navigate(item.path);
58
+ }
59
+ };
60
+
61
+ return (
62
+ <Box>
63
+ <Box mb={4}>
64
+ <Typography variant="h2" gutterBottom fontWeight="bold" mb={2}>
65
+ {t('common.welcome')}
66
+ </Typography>
67
+ <Typography variant="body1" color="text.secondary" mb={2}>
68
+ {t('common.welcomeDesc')}
69
+ </Typography>
70
+ </Box>
71
+
72
+ <Typography variant="h5" gutterBottom>
73
+ {t('integrations.basicFeatures')} 🚀
74
+ </Typography>
75
+ <Grid container spacing={3} mb={4}>
76
+ {basicFeatures.map((item) => (
77
+ <Grid item xs={12} sm={6} md={4} key={item.path}>
78
+ <Card sx={{ height: '100%' }}>
79
+ <CardActionArea onClick={() => handleClick(item)} sx={{ height: '100%' }}>
80
+ <CardContent>
81
+ <Box display="flex" alignItems="center" gap={2} mb={2}>
82
+ {item.icon}
83
+ <Typography variant="h6">{t(item.title)}</Typography>
84
+ </Box>
85
+ <Typography variant="body2" color="text.secondary">
86
+ {t(item.description)}
87
+ </Typography>
88
+ </CardContent>
89
+ </CardActionArea>
90
+ </Card>
91
+ </Grid>
92
+ ))}
93
+ </Grid>
94
+
95
+ <Divider sx={{ my: 4 }} />
96
+
97
+ <Typography variant="h5" gutterBottom>
98
+ {t('integrations.advancedFeatures')} 🔥
99
+ </Typography>
100
+ <Grid container spacing={3}>
101
+ {advancedFeatures.map((item) => (
102
+ <Grid item xs={12} sm={6} md={4} key={item.path}>
103
+ <Card sx={{ height: '100%' }}>
104
+ <CardActionArea onClick={() => handleClick(item)} sx={{ height: '100%' }}>
105
+ <CardContent>
106
+ <Box display="flex" alignItems="center" gap={2} mb={2}>
107
+ {item.icon}
108
+ <Typography variant="h6">{t(item.title)}</Typography>
109
+ </Box>
110
+ <Typography variant="body2" color="text.secondary">
111
+ {t(item.description)}
112
+ </Typography>
113
+ </CardContent>
114
+ </CardActionArea>
115
+ </Card>
116
+ </Grid>
117
+ ))}
118
+ </Grid>
119
+ </Box>
120
+ );
121
+ }