payment-kit 1.19.9 → 1.19.10

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.
@@ -24,6 +24,10 @@ export const sequelizeOptionsPoolMax: number = process.env.SEQUELIZE_OPTIONS_POO
24
24
  export const sequelizeOptionsPoolIdle: number = process.env.SEQUELIZE_OPTIONS_POOL_IDLE
25
25
  ? +process.env.SEQUELIZE_OPTIONS_POOL_IDLE
26
26
  : 10 * 1000;
27
+
28
+ export const updateDataConcurrency: number = process.env.UPDATE_DATA_CONCURRENCY
29
+ ? +process.env.UPDATE_DATA_CONCURRENCY
30
+ : 5; // 默认并发数为 5
27
31
  export default {
28
32
  ...env,
29
33
  };
@@ -70,13 +70,6 @@ export async function checkTokenBalance(args: {
70
70
  let delegator = userDid;
71
71
  if (!args.skipUserCheck) {
72
72
  const { user } = await blocklet.getUser(userDid, { enableConnectedAccount: true });
73
- if (!user) {
74
- return {
75
- sufficient: false,
76
- reason: 'NO_CUSTOMER',
77
- requestedAmount: totalAmount.toString(),
78
- };
79
- }
80
73
  if (user) {
81
74
  delegator = getWalletDid(user);
82
75
  }
@@ -216,6 +209,9 @@ export async function isDelegationSufficientForPayment(args: {
216
209
  lineItems?: TLineItemExpanded[];
217
210
  }): Promise<SufficientForPaymentResult> {
218
211
  const { paymentCurrency, paymentMethod, userDid, amount, delegatorAmounts } = args;
212
+ if (!userDid) {
213
+ return { sufficient: false, reason: 'NO_DID_WALLET' };
214
+ }
219
215
  const tokenAddress = paymentCurrency.contract as string;
220
216
 
221
217
  let totalAmount = new BN(amount);
@@ -14,6 +14,7 @@ import uniq from 'lodash/uniq';
14
14
  import type { WhereOptions } from 'sequelize';
15
15
 
16
16
  import { CustomError, formatError, getStatusFromError } from '@blocklet/error';
17
+ import pAll from 'p-all';
17
18
  import { MetadataSchema } from '../libs/api';
18
19
  import { checkPassportForPaymentLink } from '../integrations/blocklet/passport';
19
20
  import dayjs from '../libs/dayjs';
@@ -89,6 +90,7 @@ import { handleStripeSubscriptionSucceed } from '../integrations/stripe/handlers
89
90
  import { CHARGE_SUPPORTED_CHAIN_TYPES } from '../libs/constants';
90
91
  import { blocklet } from '../libs/auth';
91
92
  import { addSubscriptionJob } from '../queues/subscription';
93
+ import { updateDataConcurrency } from '../libs/env';
92
94
 
93
95
  const router = Router();
94
96
 
@@ -110,7 +112,7 @@ const getPaymentTypes = async (items: any[]) => {
110
112
  };
111
113
 
112
114
  export async function validateInventory(line_items: LineItem[], includePendingQuantity = false) {
113
- const checks = line_items.map(async (item) => {
115
+ const checks = line_items.map((item) => async () => {
114
116
  const priceId = item.price_id;
115
117
  const quantity = Number(item.quantity || 0);
116
118
 
@@ -156,7 +158,7 @@ export async function validateInventory(line_items: LineItem[], includePendingQu
156
158
  throw new Error(`Can not exceed available quantity for price: ${priceId}`);
157
159
  }
158
160
  });
159
- await Promise.all(checks);
161
+ await pAll(checks, { concurrency: updateDataConcurrency });
160
162
  }
161
163
 
162
164
  export async function validatePaymentSettings(paymentMethodId: string, paymentCurrencyId: string) {
@@ -624,11 +626,12 @@ async function processSubscriptionFastCheckout({
624
626
  }> {
625
627
  try {
626
628
  const primarySubscription = subscriptions.find((x) => x.metadata?.is_primary_subscription) || subscriptions[0];
627
- const subscriptionAmounts = await Promise.all(
628
- subscriptions.map(async (sub) => {
629
+ const subscriptionAmounts = await pAll(
630
+ subscriptions.map((sub) => async () => {
629
631
  const subItems = await getSubscriptionLineItems(sub, lineItems, primarySubscription);
630
632
  return getFastCheckoutAmount(subItems, 'subscription', paymentCurrency.id, trialEnd > now);
631
- })
633
+ }),
634
+ { concurrency: updateDataConcurrency }
632
635
  );
633
636
  const totalAmount = subscriptionAmounts
634
637
  .reduce((sum: BN, amt: string) => sum.add(new BN(amt)), new BN('0'))
@@ -669,15 +672,23 @@ async function processSubscriptionFastCheckout({
669
672
 
670
673
  if (executePayment) {
671
674
  // Update payment settings for all subscriptions
672
- await Promise.all(subscriptions.map((sub) => sub.update({ payment_settings: paymentSettings })));
675
+ await pAll(
676
+ subscriptions.map((sub) => async () => {
677
+ await sub.update({
678
+ payment_settings: paymentSettings,
679
+ payment_details: { [paymentMethod.type]: { payer: customer.did } },
680
+ });
681
+ }),
682
+ { concurrency: updateDataConcurrency }
683
+ );
673
684
  if (paymentCurrency.isCredit()) {
674
685
  // skip invoice creation for credit subscriptions
675
686
  checkoutSession.update({
676
687
  status: 'complete',
677
688
  payment_status: 'paid',
678
689
  });
679
- await Promise.all(
680
- subscriptions.map(async (sub) => {
690
+ await pAll(
691
+ subscriptions.map((sub) => async () => {
681
692
  await sub.update({
682
693
  payment_settings: paymentSettings,
683
694
  status: sub.trial_end ? 'trialing' : 'active',
@@ -688,8 +699,9 @@ async function processSubscriptionFastCheckout({
688
699
  },
689
700
  },
690
701
  });
691
- addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
692
- })
702
+ await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
703
+ }),
704
+ { concurrency: updateDataConcurrency }
693
705
  );
694
706
  return {
695
707
  success: true,
@@ -705,17 +717,22 @@ async function processSubscriptionFastCheckout({
705
717
  subscriptions,
706
718
  });
707
719
  // Update invoice settings and push to queue
708
- await Promise.all(
709
- invoices.map(async (invoice) => {
720
+ await pAll(
721
+ invoices.map((invoice) => async () => {
710
722
  if (invoice) {
711
723
  await invoice.update({ auto_advance: true, payment_settings: paymentSettings });
712
- invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
724
+ return invoiceQueue.push({ id: invoice.id, job: { invoiceId: invoice.id, retryOnError: false } });
713
725
  }
714
- })
726
+ }),
727
+ { concurrency: updateDataConcurrency }
715
728
  );
716
-
717
729
  // Add subscription cycle jobs
718
- await Promise.all(subscriptions.map((sub) => addSubscriptionJob(sub, 'cycle', false, sub.trial_end)));
730
+ await pAll(
731
+ subscriptions.map((sub) => async () => {
732
+ await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
733
+ }),
734
+ { concurrency: updateDataConcurrency }
735
+ );
719
736
 
720
737
  logger.info('Created and queued invoices for fast checkout with subscriptions', {
721
738
  checkoutSessionId: checkoutSession.id,
@@ -1382,8 +1399,8 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
1382
1399
  };
1383
1400
  }
1384
1401
  stripeContext.stripe_subscriptions = '';
1385
- await Promise.all(
1386
- subscriptions.map(async (sub) => {
1402
+ await pAll(
1403
+ subscriptions.map((sub) => async () => {
1387
1404
  const subscriptionItems = await SubscriptionItem.findAll({ where: { subscription_id: sub.id } });
1388
1405
  let stripeItems = lineItems.filter((x) =>
1389
1406
  subscriptionItems.some((y) => y.price_id === x.price_id || y.price_id === x.upsell_price_id)
@@ -1438,7 +1455,8 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
1438
1455
  stripeSubscription,
1439
1456
  subscription: sub,
1440
1457
  };
1441
- })
1458
+ }),
1459
+ { concurrency: updateDataConcurrency }
1442
1460
  );
1443
1461
  if (subscriptions.length > 1) {
1444
1462
  stripeContext.has_multiple_subscriptions = true;
@@ -1707,8 +1725,8 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
1707
1725
  status: 'complete',
1708
1726
  payment_status: 'paid',
1709
1727
  });
1710
- await Promise.all(
1711
- subscriptions.map(async (sub) => {
1728
+ await pAll(
1729
+ subscriptions.map((sub) => async () => {
1712
1730
  await sub.update({
1713
1731
  payment_settings: paymentSettings,
1714
1732
  status: sub.trial_end ? 'trialing' : 'active',
@@ -1719,8 +1737,9 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
1719
1737
  },
1720
1738
  },
1721
1739
  });
1722
- addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
1723
- })
1740
+ await addSubscriptionJob(sub, 'cycle', false, sub.trial_end);
1741
+ }),
1742
+ { concurrency: updateDataConcurrency }
1724
1743
  );
1725
1744
  delegation = {
1726
1745
  sufficient: true,
@@ -28,7 +28,7 @@ export default {
28
28
  },
29
29
  onConnect: async ({ userDid, userPk, extraParams }: CallbackArgs) => {
30
30
  const { subscriptionId } = extraParams;
31
- const { subscription, paymentMethod, paymentCurrency, customer } = await ensureChangePaymentContext(subscriptionId);
31
+ const { subscription, paymentMethod, paymentCurrency } = await ensureChangePaymentContext(subscriptionId);
32
32
 
33
33
  const claimsList: any[] = [];
34
34
  // @ts-ignore
@@ -41,7 +41,7 @@ export default {
41
41
  const delegation = await isDelegationSufficientForPayment({
42
42
  paymentMethod,
43
43
  paymentCurrency,
44
- userDid: customer!.did,
44
+ userDid,
45
45
  amount: fastCheckoutAmount,
46
46
  });
47
47
  const needDelegation = delegation.sufficient === false;
@@ -30,7 +30,7 @@ export default {
30
30
  },
31
31
  onConnect: async ({ userDid, userPk, extraParams }: CallbackArgs) => {
32
32
  const { subscriptionId } = extraParams;
33
- const { paymentMethod, paymentCurrency, subscription, customer } = await ensureSubscription(subscriptionId);
33
+ const { paymentMethod, paymentCurrency, subscription } = await ensureSubscription(subscriptionId);
34
34
 
35
35
  const claimsList: any[] = [];
36
36
  // @ts-ignore
@@ -43,7 +43,7 @@ export default {
43
43
  const delegation = await isDelegationSufficientForPayment({
44
44
  paymentMethod,
45
45
  paymentCurrency,
46
- userDid: customer!.did,
46
+ userDid,
47
47
  amount: fastCheckoutAmount,
48
48
  });
49
49
 
@@ -33,7 +33,7 @@ export default {
33
33
  onConnect: async (args: CallbackArgs) => {
34
34
  const { userDid, userPk, extraParams } = args;
35
35
  const { checkoutSessionId } = extraParams;
36
- const { paymentMethod, paymentCurrency, checkoutSession, subscription, customer } =
36
+ const { paymentMethod, paymentCurrency, checkoutSession, subscription } =
37
37
  await ensureSetupIntent(checkoutSessionId);
38
38
  if (!subscription) {
39
39
  throw new Error('Subscription for checkoutSession not found');
@@ -56,7 +56,7 @@ export default {
56
56
  const delegation = await isDelegationSufficientForPayment({
57
57
  paymentMethod,
58
58
  paymentCurrency,
59
- userDid: customer.did,
59
+ userDid,
60
60
  amount: fastCheckoutAmount,
61
61
  });
62
62
  // if we can complete purchase without any wallet interaction
@@ -1,3 +1,4 @@
1
+ import pAll from 'p-all';
1
2
  import { broadcastEvmTransaction, executeEvmTransaction, waitForEvmTxConfirm } from '../../integrations/ethereum/tx';
2
3
  import type { CallbackArgs } from '../../libs/auth';
3
4
  import dayjs from '../../libs/dayjs';
@@ -19,13 +20,24 @@ import {
19
20
  } from './shared';
20
21
  import { ensureStakeInvoice } from '../../libs/invoice';
21
22
  import { EVM_CHAIN_TYPES } from '../../libs/constants';
23
+ import { updateDataConcurrency } from '../../libs/env';
22
24
 
23
25
  const updateInvoices = async (invoices: Invoice[], update: Partial<Invoice>) => {
24
- await Promise.all(invoices.map((invoice) => invoice.update(update)));
26
+ await pAll(
27
+ invoices.map((invoice) => async () => {
28
+ await invoice.update(update);
29
+ }),
30
+ { concurrency: updateDataConcurrency }
31
+ );
25
32
  };
26
33
 
27
34
  const updateSubscriptions = async (subscriptions: Subscription[], update: Partial<Subscription>) => {
28
- await Promise.all(subscriptions.map((subscription) => subscription.update(update)));
35
+ await pAll(
36
+ subscriptions.map((subscription) => async () => {
37
+ await subscription.update(update);
38
+ }),
39
+ { concurrency: updateDataConcurrency }
40
+ );
29
41
  };
30
42
 
31
43
  export default {
@@ -46,7 +58,6 @@ export default {
46
58
  paymentMethod,
47
59
  paymentCurrency,
48
60
  subscriptions,
49
- customer,
50
61
  subscription: primarySubscription,
51
62
  } = await ensurePaymentIntent(checkoutSessionId, connectedDid || sessionUserDid || userDid);
52
63
  if (!subscriptions || subscriptions.length === 0) {
@@ -71,7 +82,7 @@ export default {
71
82
  const delegation = await isDelegationSufficientForPayment({
72
83
  paymentMethod,
73
84
  paymentCurrency,
74
- userDid: customer.did,
85
+ userDid,
75
86
  amount: fastCheckoutAmount,
76
87
  });
77
88
 
@@ -208,8 +219,11 @@ export default {
208
219
  }
209
220
  }
210
221
 
211
- await Promise.all(
212
- subscriptions.map((subscription) => addSubscriptionJob(subscription, 'cycle', false, subscription.trial_end))
222
+ await pAll(
223
+ subscriptions.map((subscription) => async () => {
224
+ await addSubscriptionJob(subscription, 'cycle', false, subscription.trial_end);
225
+ }),
226
+ { concurrency: updateDataConcurrency }
213
227
  );
214
228
 
215
229
  logger.info('CheckoutSession updated with multiple subscriptions', {
@@ -1052,10 +1052,11 @@ router.put('/:id', authPortal, async (req, res) => {
1052
1052
  if (due === '0') {
1053
1053
  hasNext = false;
1054
1054
  } else {
1055
+ const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
1055
1056
  const delegation = await isDelegationSufficientForPayment({
1056
1057
  paymentMethod,
1057
1058
  paymentCurrency,
1058
- userDid: customer.did,
1059
+ userDid: payer,
1059
1060
  amount: setup.amount.setup,
1060
1061
  });
1061
1062
  if (delegation.sufficient) {
@@ -1657,10 +1658,11 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
1657
1658
  }
1658
1659
  } else {
1659
1660
  // changing from crypto to crypto: just update the subscription
1661
+ const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
1660
1662
  delegation = await isDelegationSufficientForPayment({
1661
1663
  paymentMethod,
1662
1664
  paymentCurrency,
1663
- userDid: customer!.did,
1665
+ userDid: payer,
1664
1666
  amount: getFastCheckoutAmount(lineItems, 'subscription', paymentCurrency.id, false),
1665
1667
  });
1666
1668
  const noStake = subscription.billing_thresholds?.no_stake;
@@ -1679,7 +1681,10 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
1679
1681
  default_payment_method_id: paymentMethod.id,
1680
1682
  payment_settings: {
1681
1683
  payment_method_types: [paymentMethod.type],
1682
- payment_method_options: {},
1684
+ payment_method_options: {
1685
+ ...(subscription.payment_settings?.payment_method_options || {}),
1686
+ [paymentMethod.type]: { payer },
1687
+ },
1683
1688
  },
1684
1689
  });
1685
1690
  logger.info('Subscription payment change done on delegation enough', {
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.19.9
17
+ version: 1.19.10
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.19.9",
3
+ "version": "1.19.10",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
@@ -55,7 +55,7 @@
55
55
  "@blocklet/error": "^0.2.5",
56
56
  "@blocklet/js-sdk": "^1.16.46",
57
57
  "@blocklet/logger": "^1.16.46",
58
- "@blocklet/payment-react": "1.19.9",
58
+ "@blocklet/payment-react": "1.19.10",
59
59
  "@blocklet/sdk": "^1.16.46",
60
60
  "@blocklet/ui-react": "^3.0.36",
61
61
  "@blocklet/uploader": "^0.2.4",
@@ -124,7 +124,7 @@
124
124
  "devDependencies": {
125
125
  "@abtnode/types": "^1.16.46",
126
126
  "@arcblock/eslint-config-ts": "^0.3.3",
127
- "@blocklet/payment-types": "1.19.9",
127
+ "@blocklet/payment-types": "1.19.10",
128
128
  "@types/cookie-parser": "^1.4.9",
129
129
  "@types/cors": "^2.8.19",
130
130
  "@types/debug": "^4.1.12",
@@ -170,5 +170,5 @@
170
170
  "parser": "typescript"
171
171
  }
172
172
  },
173
- "gitHead": "a65b166cee114b432be7c1e95b8ef7bc88b30fbd"
173
+ "gitHead": "96665501b84f130e074df9a3d70f9b6f48cc3309"
174
174
  }
@@ -4,10 +4,11 @@ import type { TProductExpanded } from '@blocklet/payment-types';
4
4
  import { AddOutlined } from '@mui/icons-material';
5
5
  import { Avatar, Box, ListSubheader, MenuItem, Select, Stack, Typography } from '@mui/material';
6
6
  import cloneDeep from 'lodash/cloneDeep';
7
- import { useState } from 'react';
7
+ import { useRef, useState } from 'react';
8
8
  import type { LiteralUnion } from 'type-fest';
9
9
 
10
10
  import Empty from '@arcblock/ux/lib/Empty';
11
+ import useBus, { EventAction } from 'use-bus';
11
12
  import { useProductsContext } from '../../contexts/products';
12
13
 
13
14
  type Props = {
@@ -53,14 +54,26 @@ export default function ProductSelect({
53
54
  const { products } = useProductsContext();
54
55
  const { settings } = usePaymentContext();
55
56
  const [value, setValue] = useState('');
57
+ const valueRef = useRef(value);
56
58
  const size = { width: 16, height: 16 };
57
59
 
58
60
  const handleSelect = (e: any) => {
59
61
  setValue(e.target.value);
62
+ valueRef.current = e.target.value;
60
63
  setMode('waiting');
61
64
  onSelect(e.target.value);
62
65
  };
63
66
 
67
+ useBus(
68
+ (event: EventAction) => event.type === 'product.select.price.removed',
69
+ (event: EventAction) => {
70
+ if (valueRef.current === event.priceId) {
71
+ setValue('');
72
+ valueRef.current = '';
73
+ }
74
+ }
75
+ );
76
+
64
77
  const items = (callback?: any) => {
65
78
  const filteredProducts = filterProducts(products, hasSelected, filterPrice);
66
79
 
@@ -5,6 +5,7 @@ import type { PricingTableItem, TProduct, TProductExpanded } from '@blocklet/pay
5
5
  import { Box, Stack } from '@mui/material';
6
6
  import { useSetState } from 'ahooks';
7
7
 
8
+ import { dispatch } from 'use-bus';
8
9
  import { useProductsContext } from '../../contexts/products';
9
10
  import { getPriceFromProducts } from '../../libs/util';
10
11
  import Actions from '../actions';
@@ -74,7 +75,13 @@ export default function ProductItem({ product, prices, valid, onUpdate, onRemove
74
75
  key={x.index}
75
76
  prefix={`items.${x.index}`}
76
77
  price={getPriceFromProducts(products, x.price_id) as any}
77
- onRemove={() => onRemove(x.index)}
78
+ onRemove={() => {
79
+ onRemove(x.index);
80
+ dispatch({
81
+ type: 'product.select.price.removed',
82
+ priceId: x.price_id,
83
+ });
84
+ }}
78
85
  />
79
86
  ))}
80
87
  </Stack>
@@ -33,7 +33,12 @@ const fetchProducts = (forceRefresh = false): Promise<{ list: TProductExpanded[]
33
33
  // eslint-disable-next-line react/prop-types
34
34
  function ProductsProvider({ children }: { children: any }): JSX.Element {
35
35
  const { data, error, run, loading } = useRequest((forceRefresh = false) => fetchProducts(forceRefresh));
36
- useBus('project.created', () => run(true), []);
36
+ useBus('product.created', () => run(true), []);
37
+ useBus('product.updated', () => run(true), []);
38
+ useBus('product.deleted', () => run(true), []);
39
+ useBus('price.created', () => run(true), []);
40
+ useBus('price.updated', () => run(true), []);
41
+ useBus('price.deleted', () => run(true), []);
37
42
  if (error) {
38
43
  return <Alert severity="error">{error.message}</Alert>;
39
44
  }
@@ -102,6 +102,16 @@ export default function PaymentLinkDetail(props: { id: string }) {
102
102
  setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
103
103
  };
104
104
 
105
+ const renderConfirmPage = (record: any) => {
106
+ if (record.type === 'hosted_confirmation') {
107
+ return <Typography>{record.hosted_confirmation.custom_message || t('common.none')}</Typography>;
108
+ }
109
+ if (record.type === 'redirect' && record.redirect.url) {
110
+ return <Link to={record.redirect.url}>{record.redirect.url}</Link>;
111
+ }
112
+ return t('common.none');
113
+ };
114
+
105
115
  return (
106
116
  <Grid container spacing={4} sx={{ mb: 4 }}>
107
117
  <Grid size={12}>
@@ -362,7 +372,10 @@ export default function PaymentLinkDetail(props: { id: string }) {
362
372
  value={data.phone_number_collection?.enabled ? t('common.yes') : t('common.no')}
363
373
  />
364
374
 
365
- <InfoRow label={t('admin.paymentLink.showConfirmPage')} value={data.after_completion?.type} />
375
+ <InfoRow
376
+ label={t('admin.paymentLink.showConfirmPage')}
377
+ value={renderConfirmPage(data.after_completion)}
378
+ />
366
379
  <InfoRow label={t('admin.paymentLink.mintNft')} value={data.nft_mint_settings?.factory || ''} />
367
380
  <InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
368
381
  <InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
@@ -6,6 +6,7 @@ import { useSetState } from 'ahooks';
6
6
  import noop from 'lodash/noop';
7
7
  import { useNavigate } from 'react-router-dom';
8
8
 
9
+ import { dispatch } from 'use-bus';
9
10
  import Actions from '../../../../components/actions';
10
11
  import EditPrice from '../../../../components/product/edit-price';
11
12
 
@@ -45,6 +46,7 @@ export default function PriceActions({
45
46
  setState({ loading: true });
46
47
  await api.put(`/api/prices/${data.id}`, updates).then((res) => res.data);
47
48
  Toast.success(t('common.saved'));
49
+ dispatch('price.updated');
48
50
  onChange(state.action);
49
51
  } catch (err) {
50
52
  console.error(err);
@@ -59,6 +61,7 @@ export default function PriceActions({
59
61
  setState({ loading: true });
60
62
  await api.put(`/api/prices/${data.id}/archive`).then((res) => res.data);
61
63
  Toast.success(t('common.saved'));
64
+ dispatch('price.updated');
62
65
  onChange(state.action);
63
66
  } catch (err) {
64
67
  console.error(err);
@@ -72,6 +75,7 @@ export default function PriceActions({
72
75
  setState({ loading: true });
73
76
  await api.delete(`/api/prices/${data.id}`).then((res) => res.data);
74
77
  Toast.success(t('common.removed'));
78
+ dispatch('price.deleted');
75
79
  onChange(state.action);
76
80
  } catch (err) {
77
81
  console.error(err);
@@ -17,6 +17,7 @@ import { styled } from '@mui/system';
17
17
  import { useRequest, useSetState } from 'ahooks';
18
18
  import { Link, useNavigate } from 'react-router-dom';
19
19
 
20
+ import { dispatch } from 'use-bus';
20
21
  import Copyable from '../../../../components/copyable';
21
22
  import Currency from '../../../../components/currency';
22
23
  import EventList from '../../../../components/event/list';
@@ -68,6 +69,7 @@ export default function PriceDetail(props: { id: string }) {
68
69
  setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
69
70
  await api.put(`/api/prices/${props.id}`, updates).then((res) => res.data);
70
71
  Toast.success(t('common.saved'));
72
+ dispatch('price.updated');
71
73
  runAsync();
72
74
  } catch (err) {
73
75
  console.error(err);
@@ -159,7 +159,7 @@ export default function ProductsCreate({
159
159
  Toast.success(t('admin.product.saved'));
160
160
  methods.reset();
161
161
  dispatch('drawer.submitted');
162
- dispatch('project.created');
162
+ dispatch('product.created');
163
163
  if (onSubmitCallback) {
164
164
  onSubmitCallback();
165
165
  }
@@ -20,6 +20,7 @@ import { useRequest, useSetState } from 'ahooks';
20
20
  import { useNavigate } from 'react-router-dom';
21
21
 
22
22
  import { isEmpty } from 'lodash';
23
+ import { dispatch } from 'use-bus';
23
24
  import Copyable from '../../../../components/copyable';
24
25
  import EventList from '../../../../components/event/list';
25
26
  import InfoMetric from '../../../../components/info-metric';
@@ -78,6 +79,7 @@ export default function ProductDetail(props: { id: string }) {
78
79
  setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
79
80
  await api.put(`/api/products/${props.id}`, updates).then((res) => res.data);
80
81
  Toast.success(t('common.saved'));
82
+ dispatch('product.updated');
81
83
  runAsync();
82
84
  } catch (err) {
83
85
  console.error(err);
@@ -92,6 +94,7 @@ export default function ProductDetail(props: { id: string }) {
92
94
  setState((prev) => ({ loading: { ...prev.loading, price: true } }));
93
95
  await api.post('/api/prices', { ...price, product_id: props.id });
94
96
  Toast.success(t('common.saved'));
97
+ dispatch('price.created');
95
98
  runAsync();
96
99
  } catch (err) {
97
100
  console.error(err);
@@ -60,7 +60,9 @@ export default function ProductsList() {
60
60
  setData(res);
61
61
  });
62
62
 
63
- useBus('project.created', () => refresh(), []);
63
+ useBus('product.created', () => refresh(), []);
64
+ useBus('product.updated', () => refresh(), []);
65
+ useBus('product.deleted', () => refresh(), []);
64
66
 
65
67
  useEffect(() => {
66
68
  refresh();