payment-kit 1.13.23 → 1.13.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.
- package/README.md +4 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
- package/api/src/integrations/stripe/handlers/payment-intent.ts +2 -2
- package/api/src/integrations/stripe/handlers/setup-intent.ts +1 -1
- package/api/src/integrations/stripe/handlers/subscription.ts +2 -2
- package/api/src/jobs/event.ts +10 -4
- package/api/src/jobs/webhook.ts +17 -8
- package/api/src/libs/audit.ts +3 -3
- package/api/src/libs/event.ts +3 -0
- package/api/src/libs/util.ts +5 -0
- package/api/src/routes/checkout-sessions.ts +3 -3
- package/api/src/routes/connect/pay.ts +1 -1
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/payment-links.ts +0 -1
- package/api/src/routes/pricing-table.ts +342 -0
- package/api/src/routes/subscriptions.ts +15 -0
- package/api/src/store/migrations/20231017-pricing-table.ts +10 -0
- package/api/src/store/models/index.ts +14 -1
- package/api/src/store/models/pricing-table.ts +107 -0
- package/api/src/store/models/types.ts +53 -0
- package/blocklet.yml +2 -2
- package/package.json +4 -3
- package/src/app.tsx +1 -1
- package/src/components/blockchain/tx.tsx +8 -0
- package/src/components/payment-link/actions.tsx +20 -9
- package/src/components/payment-link/chrome.tsx +5 -3
- package/src/components/payment-link/preview.tsx +8 -5
- package/src/components/payment-link/rename.tsx +3 -3
- package/src/components/price/form.tsx +4 -1
- package/src/components/pricing-table/actions.tsx +126 -0
- package/src/components/pricing-table/customer-settings.tsx +17 -0
- package/src/components/pricing-table/payment-settings.tsx +179 -0
- package/src/components/pricing-table/preview.tsx +34 -0
- package/src/components/pricing-table/price-item.tsx +64 -0
- package/src/components/pricing-table/product-item.tsx +86 -0
- package/src/components/pricing-table/product-settings.tsx +195 -0
- package/src/components/pricing-table/rename.tsx +67 -0
- package/src/libs/util.ts +54 -5
- package/src/locales/en.tsx +28 -0
- package/src/pages/admin/payments/links/create.tsx +1 -1
- package/src/pages/admin/products/index.tsx +8 -13
- package/src/pages/admin/products/pricing-tables/create.tsx +140 -0
- package/src/pages/admin/products/pricing-tables/detail.tsx +237 -0
- package/src/pages/admin/products/pricing-tables/index.tsx +154 -0
- package/src/pages/admin/products/products/create.tsx +8 -4
- package/src/pages/checkout/index.tsx +2 -1
- package/src/pages/checkout/pricing-table.tsx +195 -0
- package/src/pages/admin/products/pricing-tables.tsx +0 -3
|
@@ -11,13 +11,14 @@ import { PaymentIntent, TPaymentIntent } from './payment-intent';
|
|
|
11
11
|
import { PaymentLink, TPaymentLink } from './payment-link';
|
|
12
12
|
import { PaymentMethod, TPaymentMethod } from './payment-method';
|
|
13
13
|
import { Price, TPrice } from './price';
|
|
14
|
+
import { PricingTable, TPricingTable } from './pricing-table';
|
|
14
15
|
import { Product, TProduct } from './product';
|
|
15
16
|
import { PromotionCode } from './promotion-code';
|
|
16
17
|
import { SetupIntent, TSetupIntent } from './setup-intent';
|
|
17
18
|
import { Subscription, TSubscription } from './subscription';
|
|
18
19
|
import { SubscriptionItem, TSubscriptionItem } from './subscription-item';
|
|
19
20
|
import { SubscriptionSchedule } from './subscription-schedule';
|
|
20
|
-
import type { LineItem } from './types';
|
|
21
|
+
import type { LineItem, PricingTableItem } from './types';
|
|
21
22
|
import { TUsageRecord, UsageRecord } from './usage-record';
|
|
22
23
|
import { TWebhookAttempt, WebhookAttempt } from './webhook-attempt';
|
|
23
24
|
import { TWebhookEndpoint, WebhookEndpoint } from './webhook-endpoint';
|
|
@@ -35,6 +36,7 @@ const models = {
|
|
|
35
36
|
PaymentLink,
|
|
36
37
|
PaymentMethod,
|
|
37
38
|
Price,
|
|
39
|
+
PricingTable,
|
|
38
40
|
Product,
|
|
39
41
|
PromotionCode,
|
|
40
42
|
SetupIntent,
|
|
@@ -71,6 +73,7 @@ export * from './payment-intent';
|
|
|
71
73
|
export * from './payment-link';
|
|
72
74
|
export * from './payment-method';
|
|
73
75
|
export * from './price';
|
|
76
|
+
export * from './pricing-table';
|
|
74
77
|
export * from './product';
|
|
75
78
|
export * from './promotion-code';
|
|
76
79
|
export * from './setup-intent';
|
|
@@ -187,3 +190,13 @@ export type TUsageRecordSummary = {
|
|
|
187
190
|
export type TSetupIntentExpanded = TSetupIntent & {
|
|
188
191
|
object: 'setup_intent';
|
|
189
192
|
};
|
|
193
|
+
|
|
194
|
+
export type TPricingTableItem = PricingTableItem & {
|
|
195
|
+
price: TPrice;
|
|
196
|
+
product: TProduct;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export type TPricingTableExpanded = TPricingTable & {
|
|
200
|
+
items: TPricingTableItem[];
|
|
201
|
+
currency: TPaymentCurrency;
|
|
202
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/lines-between-class-members */
|
|
2
|
+
import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
|
|
3
|
+
import type { LiteralUnion } from 'type-fest';
|
|
4
|
+
|
|
5
|
+
import { createEvent } from '../../libs/audit';
|
|
6
|
+
import { createIdGenerator } from '../../libs/util';
|
|
7
|
+
import type { BrandSettings, PricingTableItem } from './types';
|
|
8
|
+
|
|
9
|
+
const nextId = createIdGenerator('prctbl', 24);
|
|
10
|
+
|
|
11
|
+
// @link https://stripe.com/docs/api/payment_links/payment_links
|
|
12
|
+
export class PricingTable extends Model<InferAttributes<PricingTable>, InferCreationAttributes<PricingTable>> {
|
|
13
|
+
// Unique identifier for the object.
|
|
14
|
+
declare id: CreationOptional<string>;
|
|
15
|
+
|
|
16
|
+
// Whether the PricingTable is currently available for purchase.
|
|
17
|
+
declare active: boolean;
|
|
18
|
+
declare livemode: boolean;
|
|
19
|
+
declare locked: boolean;
|
|
20
|
+
|
|
21
|
+
declare name: string;
|
|
22
|
+
|
|
23
|
+
// The items representing what is being sold. Each line item represents an item being sold. Up to 20 line items are supported.
|
|
24
|
+
declare items: PricingTableItem[];
|
|
25
|
+
|
|
26
|
+
declare branding_settings: BrandSettings;
|
|
27
|
+
|
|
28
|
+
declare metadata?: Record<string, any>;
|
|
29
|
+
|
|
30
|
+
declare created_at: CreationOptional<Date>;
|
|
31
|
+
declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;
|
|
32
|
+
declare updated_at: CreationOptional<Date>;
|
|
33
|
+
|
|
34
|
+
public static readonly GENESIS_ATTRIBUTES = {
|
|
35
|
+
id: {
|
|
36
|
+
type: DataTypes.STRING(42),
|
|
37
|
+
primaryKey: true,
|
|
38
|
+
allowNull: false,
|
|
39
|
+
defaultValue: nextId,
|
|
40
|
+
},
|
|
41
|
+
active: {
|
|
42
|
+
type: DataTypes.BOOLEAN,
|
|
43
|
+
allowNull: false,
|
|
44
|
+
},
|
|
45
|
+
livemode: {
|
|
46
|
+
type: DataTypes.BOOLEAN,
|
|
47
|
+
allowNull: false,
|
|
48
|
+
},
|
|
49
|
+
locked: {
|
|
50
|
+
type: DataTypes.BOOLEAN,
|
|
51
|
+
allowNull: false,
|
|
52
|
+
},
|
|
53
|
+
name: {
|
|
54
|
+
type: DataTypes.STRING(255),
|
|
55
|
+
allowNull: true,
|
|
56
|
+
},
|
|
57
|
+
items: {
|
|
58
|
+
type: DataTypes.JSON,
|
|
59
|
+
defaultValue: [],
|
|
60
|
+
},
|
|
61
|
+
branding_settings: {
|
|
62
|
+
type: DataTypes.JSON,
|
|
63
|
+
allowNull: false,
|
|
64
|
+
},
|
|
65
|
+
metadata: {
|
|
66
|
+
type: DataTypes.JSON,
|
|
67
|
+
allowNull: true,
|
|
68
|
+
},
|
|
69
|
+
created_at: {
|
|
70
|
+
type: DataTypes.DATE,
|
|
71
|
+
defaultValue: DataTypes.NOW,
|
|
72
|
+
allowNull: false,
|
|
73
|
+
},
|
|
74
|
+
created_via: {
|
|
75
|
+
type: DataTypes.ENUM('api', 'dashboard', 'portal'),
|
|
76
|
+
},
|
|
77
|
+
updated_at: {
|
|
78
|
+
type: DataTypes.DATE,
|
|
79
|
+
defaultValue: DataTypes.NOW,
|
|
80
|
+
allowNull: false,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
public static initialize(sequelize: any) {
|
|
85
|
+
this.init(PricingTable.GENESIS_ATTRIBUTES, {
|
|
86
|
+
sequelize,
|
|
87
|
+
modelName: 'PricingTable',
|
|
88
|
+
tableName: 'pricing_tables',
|
|
89
|
+
createdAt: 'created_at',
|
|
90
|
+
updatedAt: 'updated_at',
|
|
91
|
+
hooks: {
|
|
92
|
+
afterCreate: (model: PricingTable, options) =>
|
|
93
|
+
createEvent('PricingTable', 'pricing_table.created', model, options).catch(console.error),
|
|
94
|
+
afterUpdate: (model: PricingTable, options) =>
|
|
95
|
+
createEvent('PricingTable', 'pricing_table.updated', model, options).catch(console.error),
|
|
96
|
+
afterDestroy: (model: PricingTable, options) =>
|
|
97
|
+
createEvent('PricingTable', 'pricing_table.deleted', model, options).catch(console.error),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public static associate() {
|
|
103
|
+
// Do nothing
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type TPricingTable = InferAttributes<PricingTable>;
|
|
@@ -262,6 +262,59 @@ export type PaymentDetails = {
|
|
|
262
262
|
};
|
|
263
263
|
};
|
|
264
264
|
|
|
265
|
+
// Very similar to PaymentLink
|
|
266
|
+
export type PricingTableItem = {
|
|
267
|
+
price_id: string;
|
|
268
|
+
product_id: string;
|
|
269
|
+
|
|
270
|
+
adjustable_quantity: {
|
|
271
|
+
enabled: boolean;
|
|
272
|
+
maximum: number;
|
|
273
|
+
minimum: number;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// The specified behavior after the purchase is complete.
|
|
277
|
+
after_completion?: AfterPayment;
|
|
278
|
+
|
|
279
|
+
// Enables user redeemable promotion codes.
|
|
280
|
+
allow_promotion_codes: boolean;
|
|
281
|
+
|
|
282
|
+
// Configuration for collecting the customer’s billing address.
|
|
283
|
+
billing_address_collection?: LiteralUnion<'auto' | 'required', string>;
|
|
284
|
+
|
|
285
|
+
is_highlight: boolean;
|
|
286
|
+
highlight_text?: LiteralUnion<'deal' | 'popular' | 'recommended', string>;
|
|
287
|
+
|
|
288
|
+
consent_collection?: {
|
|
289
|
+
promotions?: LiteralUnion<'auto' | 'none', string>;
|
|
290
|
+
terms_of_service?: LiteralUnion<'required' | 'none', string>;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// Collect additional information from your customer using custom fields. Up to 2 fields are supported.
|
|
294
|
+
custom_fields: CustomField[];
|
|
295
|
+
|
|
296
|
+
// Controls phone number collection settings during checkout.
|
|
297
|
+
phone_number_collection?: {
|
|
298
|
+
enabled: boolean;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// Describes the type of transaction being performed in order to customize relevant text
|
|
302
|
+
submit_type: LiteralUnion<'auto' | 'book' | 'donate' | 'pay', string>;
|
|
303
|
+
|
|
304
|
+
// When creating a subscription, the specified configuration data will be used.
|
|
305
|
+
subscription_data?: {
|
|
306
|
+
description: string;
|
|
307
|
+
trial_period_days: number;
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export type BrandSettings = {
|
|
312
|
+
background_color: string;
|
|
313
|
+
border_style: string;
|
|
314
|
+
button_color: string;
|
|
315
|
+
font_family: string;
|
|
316
|
+
};
|
|
317
|
+
|
|
265
318
|
export type EventType = LiteralUnion<
|
|
266
319
|
| 'account.application.authorized'
|
|
267
320
|
| 'account.application.deauthorized'
|
package/blocklet.yml
CHANGED
|
@@ -14,7 +14,7 @@ repository:
|
|
|
14
14
|
type: git
|
|
15
15
|
url: git+https://github.com/blocklet/payment-kit.git
|
|
16
16
|
specVersion: 1.2.8
|
|
17
|
-
version: 1.13.
|
|
17
|
+
version: 1.13.25
|
|
18
18
|
logo: logo.png
|
|
19
19
|
files:
|
|
20
20
|
- dist
|
|
@@ -40,7 +40,7 @@ payment:
|
|
|
40
40
|
timeout:
|
|
41
41
|
start: 60
|
|
42
42
|
requirements:
|
|
43
|
-
server: '>=1.16.
|
|
43
|
+
server: '>=1.16.10'
|
|
44
44
|
os: '*'
|
|
45
45
|
cpu: '*'
|
|
46
46
|
scripts:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.25",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"axios": "^0.27.2",
|
|
65
65
|
"body-parser": "^1.20.2",
|
|
66
66
|
"cookie-parser": "^1.4.6",
|
|
67
|
+
"copy-to-clipboard": "^3.3.3",
|
|
67
68
|
"cors": "^2.8.5",
|
|
68
69
|
"dayjs": "^1.11.10",
|
|
69
70
|
"dotenv-flow": "^3.3.0",
|
|
@@ -100,7 +101,7 @@
|
|
|
100
101
|
"devDependencies": {
|
|
101
102
|
"@arcblock/eslint-config": "^0.2.4",
|
|
102
103
|
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
103
|
-
"@did-pay/types": "1.13.
|
|
104
|
+
"@did-pay/types": "1.13.25",
|
|
104
105
|
"@types/cookie-parser": "^1.4.4",
|
|
105
106
|
"@types/cors": "^2.8.14",
|
|
106
107
|
"@types/dotenv-flow": "^3.3.1",
|
|
@@ -137,5 +138,5 @@
|
|
|
137
138
|
"parser": "typescript"
|
|
138
139
|
}
|
|
139
140
|
},
|
|
140
|
-
"gitHead": "
|
|
141
|
+
"gitHead": "4032a368047c93d3fc52ecbaa4cf37221924f45c"
|
|
141
142
|
}
|
package/src/app.tsx
CHANGED
|
@@ -38,6 +38,14 @@ const getTxLink = (method: TPaymentMethod, details: PaymentDetails) => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export default function TxLink(props: { details: PaymentDetails; method: TPaymentMethod }) {
|
|
41
|
+
if (!props.details) {
|
|
42
|
+
return (
|
|
43
|
+
<Typography component="small" color="text.secondary">
|
|
44
|
+
None
|
|
45
|
+
</Typography>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
const { text, link } = getTxLink(props.method, props.details);
|
|
42
50
|
return (
|
|
43
51
|
<Link href={link} target="_blank" rel="noopener noreferrer">
|
|
@@ -2,7 +2,9 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
|
2
2
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
3
3
|
import type { TPaymentLinkExpanded } from '@did-pay/types';
|
|
4
4
|
import { useSetState } from 'ahooks';
|
|
5
|
+
import Copy from 'copy-to-clipboard';
|
|
5
6
|
import type { LiteralUnion } from 'type-fest';
|
|
7
|
+
import { joinURL } from 'ufo';
|
|
6
8
|
|
|
7
9
|
import api from '../../libs/api';
|
|
8
10
|
import { formatError } from '../../libs/util';
|
|
@@ -54,7 +56,7 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
54
56
|
setState({ loading: false, action: '' });
|
|
55
57
|
}
|
|
56
58
|
};
|
|
57
|
-
const
|
|
59
|
+
const onRemove = async () => {
|
|
58
60
|
try {
|
|
59
61
|
setState({ loading: true });
|
|
60
62
|
await api.delete(`/api/payment-links/${data.id}`).then((res) => res.data);
|
|
@@ -67,18 +69,27 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
67
69
|
setState({ loading: false, action: '' });
|
|
68
70
|
}
|
|
69
71
|
};
|
|
72
|
+
const onCopyLink = () => {
|
|
73
|
+
Copy(joinURL(window.blocklet.appUrl, window.blocklet.prefix, `/checkout/pay/${data.id}`));
|
|
74
|
+
Toast.success(t('common.copied'));
|
|
75
|
+
};
|
|
70
76
|
|
|
71
77
|
return (
|
|
72
78
|
<ClickBoundary>
|
|
73
79
|
<Actions
|
|
74
80
|
variant={variant}
|
|
75
81
|
actions={[
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
{
|
|
83
|
+
label: t('admin.paymentLink.edit'),
|
|
84
|
+
handler: () => setState({ action: 'edit' }),
|
|
85
|
+
color: 'primary',
|
|
86
|
+
disabled: true,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: t('admin.paymentLink.copyLink'),
|
|
90
|
+
handler: onCopyLink,
|
|
91
|
+
color: 'primary',
|
|
92
|
+
},
|
|
82
93
|
{ label: t('admin.paymentLink.rename'), handler: () => setState({ action: 'rename' }), color: 'primary' },
|
|
83
94
|
{ label: t('admin.paymentLink.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' },
|
|
84
95
|
{ label: t('admin.paymentLink.remove'), handler: () => setState({ action: 'remove' }), color: 'error' },
|
|
@@ -86,7 +97,7 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
86
97
|
/>
|
|
87
98
|
{state.action === 'rename' && (
|
|
88
99
|
<RenamePaymentLink
|
|
89
|
-
|
|
100
|
+
data={data}
|
|
90
101
|
loading={state.loading}
|
|
91
102
|
onSave={onUpdate}
|
|
92
103
|
onCancel={() => setState({ action: '' })}
|
|
@@ -103,7 +114,7 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
|
|
|
103
114
|
)}
|
|
104
115
|
{state.action === 'remove' && (
|
|
105
116
|
<ConfirmDialog
|
|
106
|
-
onConfirm={
|
|
117
|
+
onConfirm={onRemove}
|
|
107
118
|
onCancel={() => setState({ action: '' })}
|
|
108
119
|
title={t('admin.paymentLink.remove')}
|
|
109
120
|
message={t('admin.paymentLink.removeTip')}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Box } from '@mui/material';
|
|
2
2
|
import { styled } from '@mui/system';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
export default function Chrome({ children, ...props }: any) {
|
|
5
|
+
return <Root {...props}>{children}</Root>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Root = styled(Box)`
|
|
5
9
|
background-color: #fcfeff;
|
|
6
10
|
border-radius: 8px;
|
|
7
11
|
margin-top: 40px;
|
|
@@ -9,5 +13,3 @@ const Chrome = styled(Box)`
|
|
|
9
13
|
overflow: hidden;
|
|
10
14
|
box-shadow: 0 20px 44px #32325d1f, 0 -1px 32px #32325d0f, 0 3px 12px #00000014;
|
|
11
15
|
`;
|
|
12
|
-
|
|
13
|
-
export default Chrome;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-unsafe-optional-chaining */
|
|
1
2
|
import { Box } from '@mui/material';
|
|
2
3
|
import { useSize } from 'ahooks';
|
|
3
4
|
import IframeResizer from 'iframe-resizer-react';
|
|
@@ -13,15 +14,17 @@ export default function PaymentLinkPreview({ id, version }: { id: string; versio
|
|
|
13
14
|
const ref = useRef(null);
|
|
14
15
|
const size = useSize(ref);
|
|
15
16
|
return (
|
|
16
|
-
<Chrome
|
|
17
|
-
<Box sx={{ width: '100%' }}
|
|
17
|
+
<Chrome>
|
|
18
|
+
<Box ref={ref} sx={{ width: '100%' }}>
|
|
19
|
+
|
|
20
|
+
</Box>
|
|
18
21
|
<IframeResizer
|
|
19
22
|
style={{
|
|
20
|
-
|
|
21
|
-
minWidth: size?.width,
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
minWidth: size?.width / 0.8,
|
|
22
25
|
minHeight: '64vh',
|
|
23
26
|
transform: 'scale(0.8)',
|
|
24
|
-
transformOrigin: '
|
|
27
|
+
transformOrigin: 'top left',
|
|
25
28
|
border: 'none',
|
|
26
29
|
}}
|
|
27
30
|
src={`${window.blocklet.prefix}checkout/pay/${id}?preview=1&version=${version}`}
|
|
@@ -8,12 +8,12 @@ import { FormProvider, useForm } from 'react-hook-form';
|
|
|
8
8
|
import TextInput from '../input';
|
|
9
9
|
|
|
10
10
|
export default function RenamePaymentLink({
|
|
11
|
-
|
|
11
|
+
data,
|
|
12
12
|
loading,
|
|
13
13
|
onSave,
|
|
14
14
|
onCancel,
|
|
15
15
|
}: {
|
|
16
|
-
|
|
16
|
+
data: TPaymentLink;
|
|
17
17
|
loading: boolean;
|
|
18
18
|
onSave: EventHandler<any>;
|
|
19
19
|
onCancel: EventHandler<any>;
|
|
@@ -21,7 +21,7 @@ export default function RenamePaymentLink({
|
|
|
21
21
|
const { t } = useLocaleContext();
|
|
22
22
|
const methods = useForm<TPaymentLink>({
|
|
23
23
|
defaultValues: {
|
|
24
|
-
name:
|
|
24
|
+
name: data.name,
|
|
25
25
|
},
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -125,7 +125,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
125
125
|
<Controller
|
|
126
126
|
name={getFieldName('unit_amount')}
|
|
127
127
|
control={control}
|
|
128
|
-
rules={{
|
|
128
|
+
rules={{
|
|
129
|
+
required: t('admin.price.unit_amount.required'),
|
|
130
|
+
validate: (v) => (Number(v) > 0 ? true : t('admin.price.unit_amount.positive')),
|
|
131
|
+
}}
|
|
129
132
|
disabled={isLocked}
|
|
130
133
|
render={({ field }) => (
|
|
131
134
|
<Box>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
3
|
+
import type { TPricingTableExpanded } from '@did-pay/types';
|
|
4
|
+
import { useSetState } from 'ahooks';
|
|
5
|
+
import Copy from 'copy-to-clipboard';
|
|
6
|
+
import type { LiteralUnion } from 'type-fest';
|
|
7
|
+
import { joinURL } from 'ufo';
|
|
8
|
+
|
|
9
|
+
import api from '../../libs/api';
|
|
10
|
+
import { formatError } from '../../libs/util';
|
|
11
|
+
import Actions from '../actions';
|
|
12
|
+
import ClickBoundary from '../click-boundary';
|
|
13
|
+
import ConfirmDialog from '../confirm';
|
|
14
|
+
import RenamePricingTable from './rename';
|
|
15
|
+
|
|
16
|
+
type Props = {
|
|
17
|
+
data: TPricingTableExpanded;
|
|
18
|
+
onChange: (action: string) => void;
|
|
19
|
+
variant?: LiteralUnion<'compact' | 'normal', string>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
PricingTableActions.defaultProps = {
|
|
23
|
+
variant: 'compact',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function PricingTableActions({ data, variant, onChange }: Props) {
|
|
27
|
+
const { t } = useLocaleContext();
|
|
28
|
+
const [state, setState] = useSetState({
|
|
29
|
+
action: '',
|
|
30
|
+
loading: false,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const onUpdate = async (updates: TPricingTableExpanded) => {
|
|
34
|
+
try {
|
|
35
|
+
setState({ loading: true });
|
|
36
|
+
await api.put(`/api/pricing-tables/${data.id}`, updates).then((res) => res.data);
|
|
37
|
+
Toast.success(t('common.saved'));
|
|
38
|
+
onChange(state.action);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error(err);
|
|
41
|
+
Toast.error(formatError(err));
|
|
42
|
+
} finally {
|
|
43
|
+
setState({ loading: false, action: '' });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const onArchive = async () => {
|
|
47
|
+
try {
|
|
48
|
+
setState({ loading: true });
|
|
49
|
+
await api.put(`/api/pricing-tables/${data.id}/archive`).then((res) => res.data);
|
|
50
|
+
Toast.success(t('common.saved'));
|
|
51
|
+
onChange(state.action);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(err);
|
|
54
|
+
Toast.error(formatError(err));
|
|
55
|
+
} finally {
|
|
56
|
+
setState({ loading: false, action: '' });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const onRemove = async () => {
|
|
60
|
+
try {
|
|
61
|
+
setState({ loading: true });
|
|
62
|
+
await api.delete(`/api/pricing-tables/${data.id}`).then((res) => res.data);
|
|
63
|
+
Toast.success(t('common.removed'));
|
|
64
|
+
onChange(state.action);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(err);
|
|
67
|
+
Toast.error(formatError(err));
|
|
68
|
+
} finally {
|
|
69
|
+
setState({ loading: false, action: '' });
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const onCopyLink = () => {
|
|
73
|
+
Copy(joinURL(window.blocklet.appUrl, window.blocklet.prefix, `/checkout/pricing-table/${data.id}`));
|
|
74
|
+
Toast.success(t('common.copied'));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<ClickBoundary>
|
|
79
|
+
<Actions
|
|
80
|
+
variant={variant}
|
|
81
|
+
actions={[
|
|
82
|
+
{
|
|
83
|
+
label: t('admin.pricingTable.edit'),
|
|
84
|
+
handler: () => setState({ action: 'edit' }),
|
|
85
|
+
color: 'primary',
|
|
86
|
+
disabled: true,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: t('admin.pricingTable.copyLink'),
|
|
90
|
+
handler: onCopyLink,
|
|
91
|
+
color: 'primary',
|
|
92
|
+
},
|
|
93
|
+
{ label: t('admin.pricingTable.rename'), handler: () => setState({ action: 'rename' }), color: 'primary' },
|
|
94
|
+
{ label: t('admin.pricingTable.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' },
|
|
95
|
+
{ label: t('admin.pricingTable.remove'), handler: () => setState({ action: 'remove' }), color: 'error' },
|
|
96
|
+
]}
|
|
97
|
+
/>
|
|
98
|
+
{state.action === 'rename' && (
|
|
99
|
+
<RenamePricingTable
|
|
100
|
+
data={data}
|
|
101
|
+
loading={state.loading}
|
|
102
|
+
onSave={onUpdate}
|
|
103
|
+
onCancel={() => setState({ action: '' })}
|
|
104
|
+
/>
|
|
105
|
+
)}
|
|
106
|
+
{state.action === 'archive' && (
|
|
107
|
+
<ConfirmDialog
|
|
108
|
+
onConfirm={onArchive}
|
|
109
|
+
onCancel={() => setState({ action: '' })}
|
|
110
|
+
title={t('admin.pricingTable.archive')}
|
|
111
|
+
message={t('admin.pricingTable.archiveTip')}
|
|
112
|
+
loading={state.loading}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
{state.action === 'remove' && (
|
|
116
|
+
<ConfirmDialog
|
|
117
|
+
onConfirm={onRemove}
|
|
118
|
+
onCancel={() => setState({ action: '' })}
|
|
119
|
+
title={t('admin.pricingTable.remove')}
|
|
120
|
+
message={t('admin.pricingTable.removeTip')}
|
|
121
|
+
loading={state.loading}
|
|
122
|
+
/>
|
|
123
|
+
)}
|
|
124
|
+
</ClickBoundary>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import { Stack, Typography } from '@mui/material';
|
|
3
|
+
|
|
4
|
+
export default function PricingTableCustomerSettings() {
|
|
5
|
+
const { t } = useLocaleContext();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<Stack spacing={2} alignItems="flex-start">
|
|
9
|
+
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
|
|
10
|
+
{t('admin.pricingTable.customer')}
|
|
11
|
+
</Typography>
|
|
12
|
+
<Stack spacing={2} sx={{ width: '100%' }}>
|
|
13
|
+
FIXME
|
|
14
|
+
</Stack>
|
|
15
|
+
</Stack>
|
|
16
|
+
);
|
|
17
|
+
}
|