@blocklet/payment-react 1.13.145 → 1.13.147

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.
@@ -1,11 +1,9 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useRequest, useSetState } from "ahooks";
3
3
  import noop from "lodash/noop";
4
- import { useEffect } from "react";
5
- import { joinURL } from "ufo";
6
4
  import api from "../api.js";
7
5
  import Payment from "../payment/index.js";
8
- import { getPrefix, mergeExtraParams } from "../util.js";
6
+ import { mergeExtraParams } from "../util.js";
9
7
  const startFromPaymentLink = async (id, params) => {
10
8
  const { data } = await api.post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`);
11
9
  return data;
@@ -23,11 +21,6 @@ export default function CheckoutForm({ id, onPaid, onError, mode, goBack, extraP
23
21
  const { error: apiError, data } = useRequest(
24
22
  () => type === "paymentLink" ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id)
25
23
  );
26
- useEffect(() => {
27
- if (type === "paymentLink" && mode === "standalone" && data) {
28
- window.location.replace(joinURL(getPrefix(), `/checkout/pay/${data.checkoutSession.id}`));
29
- }
30
- }, [type, mode, data]);
31
24
  const handlePaid = () => {
32
25
  setState({ completed: true });
33
26
  onPaid?.(data);
@@ -6,7 +6,6 @@ import { LoadingButton } from "@mui/lab";
6
6
  import {
7
7
  Box,
8
8
  Chip,
9
- Fade,
10
9
  List,
11
10
  ListItem,
12
11
  ListItemIcon,
@@ -18,7 +17,7 @@ import {
18
17
  } from "@mui/material";
19
18
  import { styled } from "@mui/system";
20
19
  import { useSetState } from "ahooks";
21
- import { useEffect } from "react";
20
+ import { useEffect, useState } from "react";
22
21
  import Amount from "../payment/amount.js";
23
22
  import { formatError, formatPriceAmount, formatRecurring } from "../util.js";
24
23
  const groupItemsByRecurring = (items) => {
@@ -41,8 +40,8 @@ PricingTable.defaultProps = {
41
40
  };
42
41
  export default function PricingTable({ table, alignItems, interval, mode, onSelect }) {
43
42
  const { t, locale } = useLocaleContext();
44
- const [state, setState] = useSetState({ interval, loading: "", loaded: false });
45
43
  const { recurring, grouped } = groupItemsByRecurring(table.items);
44
+ const [state, setState] = useSetState({ interval });
46
45
  useEffect(() => {
47
46
  if (table) {
48
47
  if (!state.interval || !grouped[state.interval]) {
@@ -53,15 +52,6 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
53
52
  }
54
53
  }
55
54
  }, [table]);
56
- const handleSelect = async (priceId) => {
57
- try {
58
- setState({ loading: priceId, loaded: true });
59
- await onSelect(priceId);
60
- } catch (err) {
61
- console.error(err);
62
- Toast.error(formatError(err));
63
- }
64
- };
65
55
  const Root = styled(Box)`
66
56
  @media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
67
57
  .price-table-item {
@@ -69,7 +59,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
69
59
  }
70
60
  }
71
61
  @media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) {
72
- .price-table-wrap:has(> div:nth-child(2)) {
62
+ .price-table-wrap:has(> div:nth-child(1)) {
73
63
  width: 300px !important;
74
64
  }
75
65
  .price-table-wrap:has(> div:nth-child(2)) {
@@ -78,6 +68,16 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
78
68
  .price-table-wrap:has(> div:nth-child(3)) {
79
69
  width: 900px !important;
80
70
  }
71
+ .price-table-wrap:has(> div:nth-child(1)) > .price-table-item {
72
+ width: 90% !important;
73
+ }
74
+ .price-table-wrap:has(> div:nth-child(2)) > .price-table-item {
75
+ // 当子元素为2的时候,子元素给宽度
76
+ width: 45% !important;
77
+ }
78
+ .price-table-wrap:has(> div:nth-child(3)) > .price-table-item {
79
+ width: 30% !important;
80
+ }
81
81
  }
82
82
  `;
83
83
  return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsxs(
@@ -124,7 +124,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
124
124
  if (mode === "select") {
125
125
  action = x.is_selected ? t("payment.checkout.selected") : t("payment.checkout.select");
126
126
  }
127
- return /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(
127
+ return /* @__PURE__ */ jsxs(
128
128
  Stack,
129
129
  {
130
130
  padding: 4,
@@ -134,7 +134,6 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
134
134
  className: "price-table-item",
135
135
  justifyContent: "center",
136
136
  sx: {
137
- width: 0.9 / grouped[state.interval].length,
138
137
  cursor: "pointer",
139
138
  borderWidth: "1px",
140
139
  borderStyle: "solid",
@@ -162,21 +161,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
162
161
  /* @__PURE__ */ jsx(Typography, { component: "span", color: "text.secondary", fontSize: "0.8rem", children: formatRecurring(x.price.recurring, false, "", locale) })
163
162
  ] })
164
163
  ] }),
165
- /* @__PURE__ */ jsx(
166
- LoadingButton,
167
- {
168
- fullWidth: true,
169
- size: "large",
170
- variant: x.is_highlight || x.is_selected ? "contained" : "outlined",
171
- color: x.is_highlight || x.is_selected ? "primary" : "info",
172
- sx: { fontSize: "1.2rem" },
173
- loading: state.loading === x.price_id && !state.loaded,
174
- disabled: x.is_disabled,
175
- onClick: () => handleSelect(x.price_id),
176
- loadingPosition: "end",
177
- children: action
178
- }
179
- ),
164
+ /* @__PURE__ */ jsx(Loading, { x, action, onSelect }),
180
165
  x.product.features.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
181
166
  /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.include") }),
182
167
  /* @__PURE__ */ jsx(List, { dense: true, children: x.product.features.map((f) => /* @__PURE__ */ jsxs(ListItem, { disableGutters: true, disablePadding: true, children: [
@@ -185,8 +170,9 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
185
170
  ] }, f.name)) })
186
171
  ] })
187
172
  ]
188
- }
189
- ) }, x.price_id);
173
+ },
174
+ x?.price_id
175
+ );
190
176
  }
191
177
  )
192
178
  }
@@ -195,3 +181,31 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
195
181
  }
196
182
  ) });
197
183
  }
184
+ function Loading({ x, action, onSelect }) {
185
+ const [state, setState] = useState({ loading: "", loaded: false });
186
+ const handleSelect = async (priceId) => {
187
+ try {
188
+ setState({ loading: priceId, loaded: true });
189
+ await onSelect(priceId);
190
+ } catch (err) {
191
+ console.error(err);
192
+ Toast.error(formatError(err));
193
+ }
194
+ };
195
+ return /* @__PURE__ */ jsx(
196
+ LoadingButton,
197
+ {
198
+ fullWidth: true,
199
+ size: "large",
200
+ variant: x.is_highlight || x.is_selected ? "contained" : "outlined",
201
+ color: x.is_highlight || x.is_selected ? "primary" : "info",
202
+ sx: { fontSize: "1.2rem" },
203
+ loading: state.loading === x.price_id && !state.loaded,
204
+ disabled: x.is_disabled,
205
+ onClick: () => handleSelect(x.price_id),
206
+ loadingPosition: "end",
207
+ endIcon: null,
208
+ children: action
209
+ }
210
+ );
211
+ }
@@ -7,8 +7,6 @@ module.exports = CheckoutForm;
7
7
  var _jsxRuntime = require("react/jsx-runtime");
8
8
  var _ahooks = require("ahooks");
9
9
  var _noop = _interopRequireDefault(require("lodash/noop"));
10
- var _react = require("react");
11
- var _ufo = require("ufo");
12
10
  var _api = _interopRequireDefault(require("../api"));
13
11
  var _payment = _interopRequireDefault(require("../payment"));
14
12
  var _util = require("../util");
@@ -45,11 +43,6 @@ function CheckoutForm({
45
43
  error: apiError,
46
44
  data
47
45
  } = (0, _ahooks.useRequest)(() => type === "paymentLink" ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id));
48
- (0, _react.useEffect)(() => {
49
- if (type === "paymentLink" && mode === "standalone" && data) {
50
- window.location.replace((0, _ufo.joinURL)((0, _util.getPrefix)(), `/checkout/pay/${data.checkoutSession.id}`));
51
- }
52
- }, [type, mode, data]);
53
46
  const handlePaid = () => {
54
47
  setState({
55
48
  completed: true
@@ -48,15 +48,13 @@ function PricingTable({
48
48
  t,
49
49
  locale
50
50
  } = (0, _context.useLocaleContext)();
51
- const [state, setState] = (0, _ahooks.useSetState)({
52
- interval,
53
- loading: "",
54
- loaded: false
55
- });
56
51
  const {
57
52
  recurring,
58
53
  grouped
59
54
  } = groupItemsByRecurring(table.items);
55
+ const [state, setState] = (0, _ahooks.useSetState)({
56
+ interval
57
+ });
60
58
  (0, _react.useEffect)(() => {
61
59
  if (table) {
62
60
  if (!state.interval || !grouped[state.interval]) {
@@ -69,18 +67,6 @@ function PricingTable({
69
67
  }
70
68
  }
71
69
  }, [table]);
72
- const handleSelect = async priceId => {
73
- try {
74
- setState({
75
- loading: priceId,
76
- loaded: true
77
- });
78
- await onSelect(priceId);
79
- } catch (err) {
80
- console.error(err);
81
- _Toast.default.error((0, _util.formatError)(err));
82
- }
83
- };
84
70
  const Root = (0, _system.styled)(_material.Box)`
85
71
  @media (max-width: ${({
86
72
  theme
@@ -92,7 +78,7 @@ function PricingTable({
92
78
  @media (min-width: ${({
93
79
  theme
94
80
  }) => theme.breakpoints.values.md}px) {
95
- .price-table-wrap:has(> div:nth-child(2)) {
81
+ .price-table-wrap:has(> div:nth-child(1)) {
96
82
  width: 300px !important;
97
83
  }
98
84
  .price-table-wrap:has(> div:nth-child(2)) {
@@ -101,6 +87,16 @@ function PricingTable({
101
87
  .price-table-wrap:has(> div:nth-child(3)) {
102
88
  width: 900px !important;
103
89
  }
90
+ .price-table-wrap:has(> div:nth-child(1)) > .price-table-item {
91
+ width: 90% !important;
92
+ }
93
+ .price-table-wrap:has(> div:nth-child(2)) > .price-table-item {
94
+ // 当子元素为2的时候,子元素给宽度
95
+ width: 45% !important;
96
+ }
97
+ .price-table-wrap:has(> div:nth-child(3)) > .price-table-item {
98
+ width: 30% !important;
99
+ }
104
100
  }
105
101
  `;
106
102
  return /* @__PURE__ */(0, _jsxRuntime.jsx)(Root, {
@@ -146,109 +142,133 @@ function PricingTable({
146
142
  if (mode === "select") {
147
143
  action = x.is_selected ? t("payment.checkout.selected") : t("payment.checkout.select");
148
144
  }
149
- return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Fade, {
150
- in: true,
151
- children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
152
- padding: 4,
153
- spacing: 2,
154
- direction: "column",
155
- alignItems: "center",
156
- className: "price-table-item",
157
- justifyContent: "center",
158
- sx: {
159
- width: 0.9 / grouped[state.interval].length,
160
- cursor: "pointer",
161
- borderWidth: "1px",
162
- borderStyle: "solid",
163
- borderColor: mode === "select" && x.is_selected ? "primary.main" : "#eee",
164
- borderRadius: 1,
165
- transition: "border-color 0.3s ease 0s, box-shadow 0.3s ease 0s",
166
- boxShadow: "0 4px 8px rgba(0, 0, 0, 20%)",
167
- "&:hover": {
168
- borderColor: mode === "select" && x.is_selected ? "primary.main" : "#ddd",
169
- boxShadow: "0 8px 16px rgba(0, 0, 0, 20%)"
170
- }
171
- },
172
- children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
173
- textAlign: "center",
174
- children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
175
- direction: "row",
176
- alignItems: "center",
177
- spacing: 1,
178
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
179
- variant: "h5",
180
- color: "text.primary",
181
- fontWeight: 600,
182
- children: x.product.name
183
- }), x.is_highlight && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Chip, {
184
- label: x.highlight_text,
185
- color: "default",
186
- size: "small"
187
- })]
188
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
189
- color: "text.secondary",
190
- children: x.product.description
191
- })]
192
- }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
145
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
146
+ padding: 4,
147
+ spacing: 2,
148
+ direction: "column",
149
+ alignItems: "center",
150
+ className: "price-table-item",
151
+ justifyContent: "center",
152
+ sx: {
153
+ cursor: "pointer",
154
+ borderWidth: "1px",
155
+ borderStyle: "solid",
156
+ borderColor: mode === "select" && x.is_selected ? "primary.main" : "#eee",
157
+ borderRadius: 1,
158
+ transition: "border-color 0.3s ease 0s, box-shadow 0.3s ease 0s",
159
+ boxShadow: "0 4px 8px rgba(0, 0, 0, 20%)",
160
+ "&:hover": {
161
+ borderColor: mode === "select" && x.is_selected ? "primary.main" : "#ddd",
162
+ boxShadow: "0 8px 16px rgba(0, 0, 0, 20%)"
163
+ }
164
+ },
165
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
166
+ textAlign: "center",
167
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
193
168
  direction: "row",
194
169
  alignItems: "center",
195
170
  spacing: 1,
196
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_amount.default, {
197
- amount: (0, _util.formatPriceAmount)(x.price, table.currency, x.product.unit_label)
198
- }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
199
- direction: "column",
200
- alignItems: "flex-start",
201
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
202
- component: "span",
203
- color: "text.secondary",
204
- fontSize: "0.8rem",
205
- children: t("payment.checkout.per")
206
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
207
- component: "span",
208
- color: "text.secondary",
209
- fontSize: "0.8rem",
210
- children: (0, _util.formatRecurring)(x.price.recurring, false, "", locale)
211
- })]
171
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
172
+ variant: "h5",
173
+ color: "text.primary",
174
+ fontWeight: 600,
175
+ children: x.product.name
176
+ }), x.is_highlight && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Chip, {
177
+ label: x.highlight_text,
178
+ color: "default",
179
+ size: "small"
212
180
  })]
213
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_lab.LoadingButton, {
214
- fullWidth: true,
215
- size: "large",
216
- variant: x.is_highlight || x.is_selected ? "contained" : "outlined",
217
- color: x.is_highlight || x.is_selected ? "primary" : "info",
218
- sx: {
219
- fontSize: "1.2rem"
220
- },
221
- loading: state.loading === x.price_id && !state.loaded,
222
- disabled: x.is_disabled,
223
- onClick: () => handleSelect(x.price_id),
224
- loadingPosition: "end",
225
- children: action
226
- }), x.product.features.length > 0 && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
181
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
182
+ color: "text.secondary",
183
+ children: x.product.description
184
+ })]
185
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
186
+ direction: "row",
187
+ alignItems: "center",
188
+ spacing: 1,
189
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_amount.default, {
190
+ amount: (0, _util.formatPriceAmount)(x.price, table.currency, x.product.unit_label)
191
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
192
+ direction: "column",
193
+ alignItems: "flex-start",
227
194
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
228
- children: t("payment.checkout.include")
229
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.List, {
230
- dense: true,
231
- children: x.product.features.map(f => /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.ListItem, {
232
- disableGutters: true,
233
- disablePadding: true,
234
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ListItemIcon, {
235
- sx: {
236
- minWidth: 25
237
- },
238
- children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.CheckOutlined, {
239
- color: "success",
240
- fontSize: "small"
241
- })
242
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ListItemText, {
243
- primary: f.name
244
- })]
245
- }, f.name))
195
+ component: "span",
196
+ color: "text.secondary",
197
+ fontSize: "0.8rem",
198
+ children: t("payment.checkout.per")
199
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
200
+ component: "span",
201
+ color: "text.secondary",
202
+ fontSize: "0.8rem",
203
+ children: (0, _util.formatRecurring)(x.price.recurring, false, "", locale)
246
204
  })]
247
205
  })]
248
- })
249
- }, x.price_id);
206
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(Loading, {
207
+ x,
208
+ action,
209
+ onSelect
210
+ }), x.product.features.length > 0 && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
211
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
212
+ children: t("payment.checkout.include")
213
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.List, {
214
+ dense: true,
215
+ children: x.product.features.map(f => /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.ListItem, {
216
+ disableGutters: true,
217
+ disablePadding: true,
218
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ListItemIcon, {
219
+ sx: {
220
+ minWidth: 25
221
+ },
222
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.CheckOutlined, {
223
+ color: "success",
224
+ fontSize: "small"
225
+ })
226
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ListItemText, {
227
+ primary: f.name
228
+ })]
229
+ }, f.name))
230
+ })]
231
+ })]
232
+ }, x?.price_id);
250
233
  })
251
234
  })]
252
235
  })
253
236
  });
237
+ }
238
+ function Loading({
239
+ x,
240
+ action,
241
+ onSelect
242
+ }) {
243
+ const [state, setState] = (0, _react.useState)({
244
+ loading: "",
245
+ loaded: false
246
+ });
247
+ const handleSelect = async priceId => {
248
+ try {
249
+ setState({
250
+ loading: priceId,
251
+ loaded: true
252
+ });
253
+ await onSelect(priceId);
254
+ } catch (err) {
255
+ console.error(err);
256
+ _Toast.default.error((0, _util.formatError)(err));
257
+ }
258
+ };
259
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_lab.LoadingButton, {
260
+ fullWidth: true,
261
+ size: "large",
262
+ variant: x.is_highlight || x.is_selected ? "contained" : "outlined",
263
+ color: x.is_highlight || x.is_selected ? "primary" : "info",
264
+ sx: {
265
+ fontSize: "1.2rem"
266
+ },
267
+ loading: state.loading === x.price_id && !state.loaded,
268
+ disabled: x.is_disabled,
269
+ onClick: () => handleSelect(x.price_id),
270
+ loadingPosition: "end",
271
+ endIcon: null,
272
+ children: action
273
+ });
254
274
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.13.145",
3
+ "version": "1.13.147",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -89,7 +89,7 @@
89
89
  "@babel/core": "^7.23.9",
90
90
  "@babel/preset-env": "^7.23.9",
91
91
  "@babel/preset-react": "^7.23.3",
92
- "@blocklet/payment-types": "1.13.145",
92
+ "@blocklet/payment-types": "1.13.147",
93
93
  "@storybook/addon-essentials": "^7.6.13",
94
94
  "@storybook/addon-interactions": "^7.6.13",
95
95
  "@storybook/addon-links": "^7.6.13",
@@ -118,5 +118,5 @@
118
118
  "vite-plugin-babel": "^1.2.0",
119
119
  "vite-plugin-node-polyfills": "^0.19.0"
120
120
  },
121
- "gitHead": "9ca5d8a2a30ae0da5630b25c409949f978a93190"
121
+ "gitHead": "9fd7b32ada89697a6e5c8b5a1c5c4729dca50c51"
122
122
  }
@@ -2,13 +2,11 @@
2
2
  import { TCheckoutSessionExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
3
3
  import { useRequest, useSetState } from 'ahooks';
4
4
  import noop from 'lodash/noop';
5
- import { useEffect } from 'react';
6
- import { joinURL } from 'ufo';
7
5
 
8
6
  import api from '../api';
9
7
  import Payment from '../payment';
10
8
  import { CheckoutContext, CheckoutProps } from '../types';
11
- import { getPrefix, mergeExtraParams } from '../util';
9
+ import { mergeExtraParams } from '../util';
12
10
 
13
11
  const startFromPaymentLink = async (id: string, params?: Record<string, any>): Promise<CheckoutContext> => {
14
12
  const { data } = await api.post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`);
@@ -34,12 +32,6 @@ export default function CheckoutForm({ id, onPaid, onError, mode, goBack, extraP
34
32
  type === 'paymentLink' ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id)
35
33
  );
36
34
 
37
- useEffect(() => {
38
- if (type === 'paymentLink' && mode === 'standalone' && data) {
39
- window.location.replace(joinURL(getPrefix(), `/checkout/pay/${data.checkoutSession.id}`));
40
- }
41
- }, [type, mode, data]);
42
-
43
35
  const handlePaid = () => {
44
36
  setState({ completed: true });
45
37
  onPaid?.(data as CheckoutContext);
@@ -7,7 +7,6 @@ import { LoadingButton } from '@mui/lab';
7
7
  import {
8
8
  Box,
9
9
  Chip,
10
- Fade,
11
10
  List,
12
11
  ListItem,
13
12
  ListItemIcon,
@@ -19,7 +18,7 @@ import {
19
18
  } from '@mui/material';
20
19
  import { styled } from '@mui/system';
21
20
  import { useSetState } from 'ahooks';
22
- import { useEffect } from 'react';
21
+ import { useEffect, useState } from 'react';
23
22
 
24
23
  import Amount from '../payment/amount';
25
24
  import { formatError, formatPriceAmount, formatRecurring } from '../util';
@@ -59,8 +58,8 @@ PricingTable.defaultProps = {
59
58
 
60
59
  export default function PricingTable({ table, alignItems, interval, mode, onSelect }: Props) {
61
60
  const { t, locale } = useLocaleContext();
62
- const [state, setState] = useSetState({ interval, loading: '', loaded: false });
63
61
  const { recurring, grouped } = groupItemsByRecurring(table.items);
62
+ const [state, setState] = useSetState({ interval });
64
63
 
65
64
  useEffect(() => {
66
65
  if (table) {
@@ -74,16 +73,6 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
74
73
  // eslint-disable-next-line react-hooks/exhaustive-deps
75
74
  }, [table]);
76
75
 
77
- const handleSelect = async (priceId: string) => {
78
- try {
79
- setState({ loading: priceId, loaded: true });
80
- await onSelect(priceId);
81
- } catch (err) {
82
- console.error(err);
83
- Toast.error(formatError(err));
84
- }
85
- };
86
-
87
76
  const Root = styled(Box)`
88
77
  @media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
89
78
  .price-table-item {
@@ -91,7 +80,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
91
80
  }
92
81
  }
93
82
  @media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) {
94
- .price-table-wrap:has(> div:nth-child(2)) {
83
+ .price-table-wrap:has(> div:nth-child(1)) {
95
84
  width: 300px !important;
96
85
  }
97
86
  .price-table-wrap:has(> div:nth-child(2)) {
@@ -100,6 +89,16 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
100
89
  .price-table-wrap:has(> div:nth-child(3)) {
101
90
  width: 900px !important;
102
91
  }
92
+ .price-table-wrap:has(> div:nth-child(1)) > .price-table-item {
93
+ width: 90% !important;
94
+ }
95
+ .price-table-wrap:has(> div:nth-child(2)) > .price-table-item {
96
+ // 当子元素为2的时候,子元素给宽度
97
+ width: 45% !important;
98
+ }
99
+ .price-table-wrap:has(> div:nth-child(3)) > .price-table-item {
100
+ width: 30% !important;
101
+ }
103
102
  }
104
103
  `;
105
104
  return (
@@ -150,77 +149,64 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
150
149
  }
151
150
 
152
151
  return (
153
- <Fade key={x.price_id} in>
154
- <Stack
155
- padding={4}
156
- spacing={2}
157
- direction="column"
158
- alignItems="center"
159
- className="price-table-item"
160
- justifyContent="center"
161
- sx={{
162
- width: 0.9 / grouped[state.interval as string].length,
163
- cursor: 'pointer',
164
- borderWidth: '1px',
165
- borderStyle: 'solid',
166
- borderColor: mode === 'select' && x.is_selected ? 'primary.main' : '#eee',
167
- borderRadius: 1,
168
- transition: 'border-color 0.3s ease 0s, box-shadow 0.3s ease 0s',
169
- boxShadow: '0 4px 8px rgba(0, 0, 0, 20%)',
170
- '&:hover': {
171
- borderColor: mode === 'select' && x.is_selected ? 'primary.main' : '#ddd',
172
- boxShadow: '0 8px 16px rgba(0, 0, 0, 20%)',
173
- },
174
- }}>
175
- <Box textAlign="center">
176
- <Stack direction="row" alignItems="center" spacing={1}>
177
- <Typography variant="h5" color="text.primary" fontWeight={600}>
178
- {x.product.name}
179
- </Typography>
180
- {x.is_highlight && <Chip label={x.highlight_text} color="default" size="small" />}
181
- </Stack>
182
- <Typography color="text.secondary">{x.product.description}</Typography>
183
- </Box>
152
+ <Stack
153
+ key={x?.price_id}
154
+ padding={4}
155
+ spacing={2}
156
+ direction="column"
157
+ alignItems="center"
158
+ className="price-table-item"
159
+ justifyContent="center"
160
+ sx={{
161
+ cursor: 'pointer',
162
+ borderWidth: '1px',
163
+ borderStyle: 'solid',
164
+ borderColor: mode === 'select' && x.is_selected ? 'primary.main' : '#eee',
165
+ borderRadius: 1,
166
+ transition: 'border-color 0.3s ease 0s, box-shadow 0.3s ease 0s',
167
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 20%)',
168
+ '&:hover': {
169
+ borderColor: mode === 'select' && x.is_selected ? 'primary.main' : '#ddd',
170
+ boxShadow: '0 8px 16px rgba(0, 0, 0, 20%)',
171
+ },
172
+ }}>
173
+ <Box textAlign="center">
184
174
  <Stack direction="row" alignItems="center" spacing={1}>
185
- <Amount amount={formatPriceAmount(x.price, table.currency, x.product.unit_label)} />
186
- <Stack direction="column" alignItems="flex-start">
187
- <Typography component="span" color="text.secondary" fontSize="0.8rem">
188
- {t('payment.checkout.per')}
189
- </Typography>
190
- <Typography component="span" color="text.secondary" fontSize="0.8rem">
191
- {formatRecurring(x.price.recurring as PriceRecurring, false, '', locale)}
192
- </Typography>
193
- </Stack>
175
+ <Typography variant="h5" color="text.primary" fontWeight={600}>
176
+ {x.product.name}
177
+ </Typography>
178
+ {x.is_highlight && <Chip label={x.highlight_text} color="default" size="small" />}
179
+ </Stack>
180
+ <Typography color="text.secondary">{x.product.description}</Typography>
181
+ </Box>
182
+ <Stack direction="row" alignItems="center" spacing={1}>
183
+ <Amount amount={formatPriceAmount(x.price, table.currency, x.product.unit_label)} />
184
+ <Stack direction="column" alignItems="flex-start">
185
+ <Typography component="span" color="text.secondary" fontSize="0.8rem">
186
+ {t('payment.checkout.per')}
187
+ </Typography>
188
+ <Typography component="span" color="text.secondary" fontSize="0.8rem">
189
+ {formatRecurring(x.price.recurring as PriceRecurring, false, '', locale)}
190
+ </Typography>
194
191
  </Stack>
195
- <LoadingButton
196
- fullWidth
197
- size="large"
198
- variant={x.is_highlight || x.is_selected ? 'contained' : 'outlined'}
199
- color={x.is_highlight || x.is_selected ? 'primary' : 'info'}
200
- sx={{ fontSize: '1.2rem' }}
201
- loading={state.loading === x.price_id && !state.loaded}
202
- disabled={x.is_disabled}
203
- onClick={() => handleSelect(x.price_id)}
204
- loadingPosition="end">
205
- {action}
206
- </LoadingButton>
207
- {x.product.features.length > 0 && (
208
- <Box>
209
- <Typography>{t('payment.checkout.include')}</Typography>
210
- <List dense>
211
- {x.product.features.map((f: any) => (
212
- <ListItem key={f.name} disableGutters disablePadding>
213
- <ListItemIcon sx={{ minWidth: 25 }}>
214
- <CheckOutlined color="success" fontSize="small" />
215
- </ListItemIcon>
216
- <ListItemText primary={f.name} />
217
- </ListItem>
218
- ))}
219
- </List>
220
- </Box>
221
- )}
222
192
  </Stack>
223
- </Fade>
193
+ <Loading x={x} action={action} onSelect={onSelect} />
194
+ {x.product.features.length > 0 && (
195
+ <Box>
196
+ <Typography>{t('payment.checkout.include')}</Typography>
197
+ <List dense>
198
+ {x.product.features.map((f: any) => (
199
+ <ListItem key={f.name} disableGutters disablePadding>
200
+ <ListItemIcon sx={{ minWidth: 25 }}>
201
+ <CheckOutlined color="success" fontSize="small" />
202
+ </ListItemIcon>
203
+ <ListItemText primary={f.name} />
204
+ </ListItem>
205
+ ))}
206
+ </List>
207
+ </Box>
208
+ )}
209
+ </Stack>
224
210
  );
225
211
  }
226
212
  )}
@@ -229,3 +215,33 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
229
215
  </Root>
230
216
  );
231
217
  }
218
+
219
+ function Loading({ x, action, onSelect }: any) {
220
+ const [state, setState] = useState({ loading: '', loaded: false });
221
+
222
+ const handleSelect = async (priceId: string) => {
223
+ try {
224
+ setState({ loading: priceId, loaded: true });
225
+ await onSelect(priceId);
226
+ } catch (err) {
227
+ console.error(err);
228
+ Toast.error(formatError(err));
229
+ }
230
+ };
231
+
232
+ return (
233
+ <LoadingButton
234
+ fullWidth
235
+ size="large"
236
+ variant={x.is_highlight || x.is_selected ? 'contained' : 'outlined'}
237
+ color={x.is_highlight || x.is_selected ? 'primary' : 'info'}
238
+ sx={{ fontSize: '1.2rem' }}
239
+ loading={state.loading === x.price_id && !state.loaded}
240
+ disabled={x.is_disabled}
241
+ onClick={() => handleSelect(x.price_id)}
242
+ loadingPosition="end"
243
+ endIcon={null}>
244
+ {action}
245
+ </LoadingButton>
246
+ );
247
+ }