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.
Files changed (48) hide show
  1. package/README.md +4 -0
  2. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  3. package/api/src/integrations/stripe/handlers/payment-intent.ts +2 -2
  4. package/api/src/integrations/stripe/handlers/setup-intent.ts +1 -1
  5. package/api/src/integrations/stripe/handlers/subscription.ts +2 -2
  6. package/api/src/jobs/event.ts +10 -4
  7. package/api/src/jobs/webhook.ts +17 -8
  8. package/api/src/libs/audit.ts +3 -3
  9. package/api/src/libs/event.ts +3 -0
  10. package/api/src/libs/util.ts +5 -0
  11. package/api/src/routes/checkout-sessions.ts +3 -3
  12. package/api/src/routes/connect/pay.ts +1 -1
  13. package/api/src/routes/index.ts +2 -0
  14. package/api/src/routes/payment-links.ts +0 -1
  15. package/api/src/routes/pricing-table.ts +342 -0
  16. package/api/src/routes/subscriptions.ts +15 -0
  17. package/api/src/store/migrations/20231017-pricing-table.ts +10 -0
  18. package/api/src/store/models/index.ts +14 -1
  19. package/api/src/store/models/pricing-table.ts +107 -0
  20. package/api/src/store/models/types.ts +53 -0
  21. package/blocklet.yml +2 -2
  22. package/package.json +4 -3
  23. package/src/app.tsx +1 -1
  24. package/src/components/blockchain/tx.tsx +8 -0
  25. package/src/components/payment-link/actions.tsx +20 -9
  26. package/src/components/payment-link/chrome.tsx +5 -3
  27. package/src/components/payment-link/preview.tsx +8 -5
  28. package/src/components/payment-link/rename.tsx +3 -3
  29. package/src/components/price/form.tsx +4 -1
  30. package/src/components/pricing-table/actions.tsx +126 -0
  31. package/src/components/pricing-table/customer-settings.tsx +17 -0
  32. package/src/components/pricing-table/payment-settings.tsx +179 -0
  33. package/src/components/pricing-table/preview.tsx +34 -0
  34. package/src/components/pricing-table/price-item.tsx +64 -0
  35. package/src/components/pricing-table/product-item.tsx +86 -0
  36. package/src/components/pricing-table/product-settings.tsx +195 -0
  37. package/src/components/pricing-table/rename.tsx +67 -0
  38. package/src/libs/util.ts +54 -5
  39. package/src/locales/en.tsx +28 -0
  40. package/src/pages/admin/payments/links/create.tsx +1 -1
  41. package/src/pages/admin/products/index.tsx +8 -13
  42. package/src/pages/admin/products/pricing-tables/create.tsx +140 -0
  43. package/src/pages/admin/products/pricing-tables/detail.tsx +237 -0
  44. package/src/pages/admin/products/pricing-tables/index.tsx +154 -0
  45. package/src/pages/admin/products/products/create.tsx +8 -4
  46. package/src/pages/checkout/index.tsx +2 -1
  47. package/src/pages/checkout/pricing-table.tsx +195 -0
  48. 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.23
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.15'
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.23",
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.23",
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": "a4ba80b4e2d020c912361af6fd555f8bfc52f54f"
141
+ "gitHead": "4032a368047c93d3fc52ecbaa4cf37221924f45c"
141
142
  }
package/src/app.tsx CHANGED
@@ -36,7 +36,7 @@ function App() {
36
36
  <ErrorBoundary FallbackComponent={ErrorFallback} onReset={window.location.reload}>
37
37
  <Suspense
38
38
  fallback={
39
- <Center relative="parent">
39
+ <Center>
40
40
  <CircularProgress />
41
41
  </Center>
42
42
  }>
@@ -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 noRemove = async () => {
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
- // label: t('admin.paymentLink.edit'),
78
- // handler: () => setState({ action: 'edit' }),
79
- // color: 'primary',
80
- // divider: true,
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
- paymentLink={data}
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={noRemove}
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
- const Chrome = styled(Box)`
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 ref={ref}>
17
- <Box sx={{ width: '100%' }}>&nbsp;</Box>
17
+ <Chrome>
18
+ <Box ref={ref} sx={{ width: '100%' }}>
19
+ &nbsp;
20
+ </Box>
18
21
  <IframeResizer
19
22
  style={{
20
- width: '1px',
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: 'center',
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
- paymentLink,
11
+ data,
12
12
  loading,
13
13
  onSave,
14
14
  onCancel,
15
15
  }: {
16
- paymentLink: TPaymentLink;
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: paymentLink.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={{ required: t('admin.price.unit_amount.required'), min: t('admin.price.unit_amount.positive') }}
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
+ }