payment-kit 1.20.11 → 1.20.13

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 (92) hide show
  1. package/api/src/crons/index.ts +8 -0
  2. package/api/src/index.ts +2 -0
  3. package/api/src/integrations/stripe/handlers/invoice.ts +63 -5
  4. package/api/src/integrations/stripe/handlers/payment-intent.ts +1 -0
  5. package/api/src/integrations/stripe/resource.ts +253 -2
  6. package/api/src/libs/currency.ts +31 -0
  7. package/api/src/libs/discount/coupon.ts +1061 -0
  8. package/api/src/libs/discount/discount.ts +349 -0
  9. package/api/src/libs/discount/nft.ts +239 -0
  10. package/api/src/libs/discount/redemption.ts +636 -0
  11. package/api/src/libs/discount/vc.ts +73 -0
  12. package/api/src/libs/env.ts +1 -0
  13. package/api/src/libs/invoice.ts +44 -10
  14. package/api/src/libs/math-utils.ts +6 -0
  15. package/api/src/libs/price.ts +43 -0
  16. package/api/src/libs/session.ts +242 -57
  17. package/api/src/libs/subscription.ts +2 -6
  18. package/api/src/libs/vendor-util/adapters/launcher-adapter.ts +1 -1
  19. package/api/src/libs/vendor-util/adapters/types.ts +1 -0
  20. package/api/src/libs/vendor-util/fulfillment.ts +1 -1
  21. package/api/src/queues/auto-recharge.ts +1 -1
  22. package/api/src/queues/discount-status.ts +200 -0
  23. package/api/src/queues/subscription.ts +98 -5
  24. package/api/src/queues/usage-record.ts +1 -1
  25. package/api/src/queues/vendors/fulfillment-coordinator.ts +1 -29
  26. package/api/src/queues/vendors/return-processor.ts +184 -0
  27. package/api/src/queues/vendors/return-scanner.ts +119 -0
  28. package/api/src/queues/vendors/status-check.ts +1 -1
  29. package/api/src/routes/auto-recharge-configs.ts +5 -3
  30. package/api/src/routes/checkout-sessions.ts +755 -64
  31. package/api/src/routes/connect/change-payment.ts +6 -1
  32. package/api/src/routes/connect/change-plan.ts +6 -1
  33. package/api/src/routes/connect/setup.ts +6 -1
  34. package/api/src/routes/connect/shared.ts +80 -9
  35. package/api/src/routes/connect/subscribe.ts +12 -2
  36. package/api/src/routes/coupons.ts +518 -0
  37. package/api/src/routes/index.ts +4 -0
  38. package/api/src/routes/invoices.ts +44 -3
  39. package/api/src/routes/meter-events.ts +2 -1
  40. package/api/src/routes/payment-currencies.ts +1 -0
  41. package/api/src/routes/promotion-codes.ts +482 -0
  42. package/api/src/routes/subscriptions.ts +23 -2
  43. package/api/src/routes/vendor.ts +89 -2
  44. package/api/src/store/migrations/20250904-discount.ts +136 -0
  45. package/api/src/store/migrations/20250910-timestamp-fields.ts +116 -0
  46. package/api/src/store/migrations/20250916-add-description-fields.ts +30 -0
  47. package/api/src/store/migrations/20250918-add-vendor-extends.ts +20 -0
  48. package/api/src/store/models/checkout-session.ts +17 -2
  49. package/api/src/store/models/coupon.ts +144 -4
  50. package/api/src/store/models/discount.ts +23 -10
  51. package/api/src/store/models/index.ts +13 -2
  52. package/api/src/store/models/product-vendor.ts +6 -0
  53. package/api/src/store/models/promotion-code.ts +295 -18
  54. package/api/src/store/models/types.ts +30 -1
  55. package/api/tests/libs/session.spec.ts +48 -27
  56. package/blocklet.yml +1 -1
  57. package/package.json +20 -20
  58. package/src/app.tsx +2 -0
  59. package/src/components/customer/link.tsx +1 -1
  60. package/src/components/discount/discount-info.tsx +178 -0
  61. package/src/components/invoice/table.tsx +140 -48
  62. package/src/components/invoice-pdf/styles.ts +6 -0
  63. package/src/components/invoice-pdf/template.tsx +59 -33
  64. package/src/components/metadata/form.tsx +14 -5
  65. package/src/components/payment-link/actions.tsx +42 -0
  66. package/src/components/price/form.tsx +91 -65
  67. package/src/components/product/vendor-config.tsx +5 -3
  68. package/src/components/promotion/active-redemptions.tsx +534 -0
  69. package/src/components/promotion/currency-multi-select.tsx +350 -0
  70. package/src/components/promotion/currency-restrictions.tsx +117 -0
  71. package/src/components/promotion/product-select.tsx +292 -0
  72. package/src/components/promotion/promotion-code-form.tsx +534 -0
  73. package/src/components/subscription/portal/list.tsx +6 -1
  74. package/src/components/subscription/vendor-service-list.tsx +13 -2
  75. package/src/locales/en.tsx +227 -0
  76. package/src/locales/zh.tsx +222 -1
  77. package/src/pages/admin/billing/subscriptions/detail.tsx +5 -0
  78. package/src/pages/admin/products/coupons/applicable-products.tsx +166 -0
  79. package/src/pages/admin/products/coupons/create.tsx +612 -0
  80. package/src/pages/admin/products/coupons/detail.tsx +538 -0
  81. package/src/pages/admin/products/coupons/edit.tsx +127 -0
  82. package/src/pages/admin/products/coupons/index.tsx +210 -3
  83. package/src/pages/admin/products/index.tsx +22 -3
  84. package/src/pages/admin/products/products/detail.tsx +12 -2
  85. package/src/pages/admin/products/promotion-codes/actions.tsx +103 -0
  86. package/src/pages/admin/products/promotion-codes/create.tsx +235 -0
  87. package/src/pages/admin/products/promotion-codes/detail.tsx +416 -0
  88. package/src/pages/admin/products/promotion-codes/list.tsx +247 -0
  89. package/src/pages/admin/products/promotion-codes/verification-config.tsx +327 -0
  90. package/src/pages/admin/products/vendors/index.tsx +17 -5
  91. package/src/pages/customer/subscription/detail.tsx +5 -0
  92. package/vite.config.ts +4 -3
@@ -1,4 +1,4 @@
1
- import { formatNumber, formatTime } from '@blocklet/payment-react';
1
+ import { formatNumber, formatTime, formatAmount } from '@blocklet/payment-react';
2
2
  import { useEffect } from 'react';
3
3
  import type { InvoicePDFProps } from './types';
4
4
  import { composeStyles } from './utils';
@@ -6,7 +6,7 @@ import { getInvoiceRows } from '../invoice/table';
6
6
  import { applyStyles } from './styles';
7
7
 
8
8
  export function InvoiceTemplate({ data, t }: InvoicePDFProps) {
9
- const { detail, summary } = getInvoiceRows(data);
9
+ const { detail, summary } = getInvoiceRows(data, t);
10
10
 
11
11
  useEffect(() => {
12
12
  applyStyles();
@@ -70,48 +70,72 @@ export function InvoiceTemplate({ data, t }: InvoicePDFProps) {
70
70
 
71
71
  {/* Table Header */}
72
72
  <div style={composeStyles('mt-40 row flex')}>
73
- <div style={composeStyles('w-48 p-4-8')}>
73
+ <div style={composeStyles('w-38 p-4-8')}>
74
74
  <span style={composeStyles('bold')}>{t('admin.subscription.product')}</span>
75
75
  </div>
76
- <div style={composeStyles('w-17 p-4-8')}>
76
+ <div style={composeStyles('w-15 p-4-8')}>
77
77
  <span style={composeStyles('bold right')}>{t('common.quantity')}</span>
78
78
  </div>
79
- <div style={composeStyles('w-17 p-4-8')}>
79
+ <div style={composeStyles('w-15 p-4-8')}>
80
80
  <span style={composeStyles('bold right')}>{t('payment.customer.invoice.unitPrice')}</span>
81
81
  </div>
82
- <div style={composeStyles('w-18 p-4-8')}>
82
+ <div style={composeStyles('w-15 p-4-8')}>
83
+ <span style={composeStyles('bold right')}>{t('common.discount')}</span>
84
+ </div>
85
+ <div style={composeStyles('w-17 p-4-8')}>
83
86
  <span style={composeStyles('bold right')}>{t('common.amount')}</span>
84
87
  </div>
85
88
  </div>
86
89
 
87
90
  {/* Table Content */}
88
- {detail.map((line) => (
89
- <div key={line.id} style={composeStyles('row flex')}>
90
- <div style={composeStyles('w-48 p-4-8 pb-15')}>
91
- <span style={composeStyles('dark')}>
92
- {line.product}
93
- {line.credits && (
94
- <span style={composeStyles('block gray fs-10 mt-5')}>
95
- {formatNumber(line.credits.total)} {line.credits.currency}
96
- </span>
97
- )}
98
- </span>
99
- </div>
100
- <div style={composeStyles('w-17 p-4-8 pb-15')}>
101
- <span style={composeStyles('dark right')}>{line.quantity}</span>
102
- </div>
103
- <div style={composeStyles('w-17 p-4-8 pb-15')}>
104
- <span style={composeStyles('dark right')}>
105
- {line.price ? `${line.price} ${data.paymentCurrency.symbol}` : ''}
106
- </span>
107
- </div>
108
- <div style={composeStyles('w-18 p-4-8 pb-15')}>
109
- <span style={composeStyles('dark right')}>
110
- {line.amount} {data.paymentCurrency.symbol}
111
- </span>
91
+ {detail.map((line) => {
92
+ // Calculate discount amount for this line item
93
+ let itemDiscountAmount = 0;
94
+ if (line.raw.discount_amounts && line.raw.discount_amounts.length > 0) {
95
+ line.raw.discount_amounts.forEach((discount: any) => {
96
+ if (discount.amount) {
97
+ itemDiscountAmount += Number(discount.amount);
98
+ }
99
+ });
100
+ }
101
+
102
+ return (
103
+ <div key={line.id} style={composeStyles('row flex')}>
104
+ <div style={composeStyles('w-38 p-4-8 pb-15')}>
105
+ <span style={composeStyles('dark')}>
106
+ {line.product}
107
+ {line.credits && (
108
+ <span style={composeStyles('block gray fs-10 mt-5')}>
109
+ {formatNumber(line.credits.total)} {line.credits.currency}
110
+ </span>
111
+ )}
112
+ </span>
113
+ </div>
114
+ <div style={composeStyles('w-15 p-4-8 pb-15')}>
115
+ <span style={composeStyles('dark right')}>{line.quantity}</span>
116
+ </div>
117
+ <div style={composeStyles('w-15 p-4-8 pb-15')}>
118
+ <span style={composeStyles('dark right')}>
119
+ {line.price ? `${line.price} ${data.paymentCurrency.symbol}` : ''}
120
+ </span>
121
+ </div>
122
+ <div style={composeStyles('w-15 p-4-8 pb-15')}>
123
+ <span style={composeStyles(`${itemDiscountAmount > 0 ? 'green' : 'dark'} right`)}>
124
+ {itemDiscountAmount > 0
125
+ ? `-${formatAmount(itemDiscountAmount.toString(), data.paymentCurrency.decimal)} ${
126
+ data.paymentCurrency.symbol
127
+ }`
128
+ : '-'}
129
+ </span>
130
+ </div>
131
+ <div style={composeStyles('w-17 p-4-8 pb-15')}>
132
+ <span style={composeStyles('dark right')}>
133
+ {line.amount} {data.paymentCurrency.symbol}
134
+ </span>
135
+ </div>
112
136
  </div>
113
- </div>
114
- ))}
137
+ );
138
+ })}
115
139
 
116
140
  {/* Summary */}
117
141
  <div style={composeStyles('flex')}>
@@ -120,7 +144,9 @@ export function InvoiceTemplate({ data, t }: InvoicePDFProps) {
120
144
  {summary.map((line) => (
121
145
  <div style={composeStyles('flex')} key={line.key}>
122
146
  <div style={composeStyles('w-60 p-5')}>
123
- <span style={composeStyles('bold')}>{t(line.key)}</span>
147
+ <span style={composeStyles('bold')}>
148
+ {line.key.startsWith('common.') || line.key.startsWith('payment.') ? t(line.key) : line.key}
149
+ </span>
124
150
  </div>
125
151
  <div style={composeStyles('w-40 p-5')}>
126
152
  <span style={composeStyles('right bold dark')}>
@@ -205,7 +205,15 @@ export default function MetadataForm({
205
205
  size="small"
206
206
  name={`metadata.${index}.key`}
207
207
  rules={{
208
- required: t('payment.checkout.required'),
208
+ required: {
209
+ value: true,
210
+ message: t('payment.checkout.required'),
211
+ },
212
+ validate: (value: any) => {
213
+ if (value === false || value === 'false') return true;
214
+ if (!value && value !== 0) return t('payment.checkout.required');
215
+ return true;
216
+ },
209
217
  maxLength: {
210
218
  value: 64,
211
219
  message: t('common.maxLength', { len: 40 }),
@@ -224,10 +232,11 @@ export default function MetadataForm({
224
232
  label="Value *"
225
233
  placeholder="Value"
226
234
  rules={{
227
- required: t('payment.checkout.required'),
228
- maxLength: {
229
- value: 256,
230
- message: t('common.maxLength', { len: 256 }),
235
+ validate: (value: any) => {
236
+ if (value === false || value === 'false') return true;
237
+ if (!value && value !== 0) return t('payment.checkout.required');
238
+ if (typeof value === 'string' && value.length > 256) return t('common.maxLength', { len: 256 });
239
+ return true;
231
240
  },
232
241
  }}
233
242
  // @ts-ignore
@@ -64,6 +64,24 @@ export default function PaymentLinkActions({ data, variant = 'compact', onChange
64
64
  setState({ loading: false, action: '' });
65
65
  }
66
66
  };
67
+
68
+ const onTogglePromotionCodes = async () => {
69
+ try {
70
+ setState({ loading: true });
71
+ await api
72
+ .put(`/api/payment-links/${data.id}`, {
73
+ allow_promotion_codes: !data.allow_promotion_codes,
74
+ })
75
+ .then((res) => res.data);
76
+ Toast.success(t('common.saved'));
77
+ onChange(state.action);
78
+ } catch (err) {
79
+ console.error(err);
80
+ Toast.error(formatError(err));
81
+ } finally {
82
+ setState({ loading: false, action: '' });
83
+ }
84
+ };
67
85
  const onCopyLink = () => {
68
86
  Copy(joinURL(window.blocklet.appUrl, window.blocklet.prefix, `/checkout/pay/${data.id}`));
69
87
  Toast.success(t('common.copied'));
@@ -102,6 +120,13 @@ export default function PaymentLinkActions({ data, variant = 'compact', onChange
102
120
  color: 'error',
103
121
  divider: true,
104
122
  },
123
+ {
124
+ label: data.allow_promotion_codes
125
+ ? t('admin.paymentLink.disablePromotionCodes')
126
+ : t('admin.paymentLink.enablePromotionCodes'),
127
+ handler: () => setState({ action: 'togglePromotionCodes' }),
128
+ color: 'primary',
129
+ },
105
130
  {
106
131
  label: t('admin.passport.assign'),
107
132
  handler: () => setState({ action: 'assign' }),
@@ -137,6 +162,23 @@ export default function PaymentLinkActions({ data, variant = 'compact', onChange
137
162
  loading={state.loading}
138
163
  />
139
164
  )}
165
+ {state.action === 'togglePromotionCodes' && (
166
+ <ConfirmDialog
167
+ onConfirm={onTogglePromotionCodes}
168
+ onCancel={() => setState({ action: '' })}
169
+ title={
170
+ data.allow_promotion_codes
171
+ ? t('admin.paymentLink.disablePromotionCodes')
172
+ : t('admin.paymentLink.enablePromotionCodes')
173
+ }
174
+ message={
175
+ data.allow_promotion_codes
176
+ ? t('admin.paymentLink.disablePromotionCodesTip')
177
+ : t('admin.paymentLink.enablePromotionCodesTip')
178
+ }
179
+ loading={state.loading}
180
+ />
181
+ )}
140
182
  </ClickBoundary>
141
183
  );
142
184
  }
@@ -352,14 +352,16 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
352
352
  !meteringMeterId ? t('admin.price.creditMetered.required') : t('admin.price.creditMetered.help')
353
353
  }
354
354
  size="small"
355
- InputProps={{
356
- ...params.InputProps,
357
- endAdornment: (
358
- <>
359
- {loadingMeters ? <div>{t('common.loading')}</div> : null}
360
- {params.InputProps.endAdornment}
361
- </>
362
- ),
355
+ slotProps={{
356
+ input: {
357
+ ...params.InputProps,
358
+ endAdornment: (
359
+ <>
360
+ {loadingMeters ? <div>{t('common.loading')}</div> : null}
361
+ {params.InputProps.endAdornment}
362
+ </>
363
+ ),
364
+ },
363
365
  }}
364
366
  />
365
367
  )}
@@ -398,35 +400,37 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
398
400
  fullWidth
399
401
  error={!!getFieldError(getFieldName('unit_amount'))}
400
402
  helperText={getFieldError(getFieldName('unit_amount'))?.message}
401
- InputProps={{
402
- endAdornment: (
403
- <InputAdornment position="end">
404
- <CurrencySelect
405
- mode="selected"
406
- hasSelected={(currency) =>
407
- currencies.fields.some((x: any) => x.currency_id === currency.id) ||
408
- currency.id === defaultCurrencyId
409
- }
410
- currencyFilter={(c) => c.type !== 'credit'}
411
- onSelect={(currencyId) => {
412
- const index = currencies.fields.findIndex(
413
- (x: any) => x.currency_id === defaultCurrencyId
414
- );
415
- if (index > -1) {
416
- // @ts-ignore
417
- handleCurrencyChange(index, currencyId);
403
+ slotProps={{
404
+ input: {
405
+ endAdornment: (
406
+ <InputAdornment position="end">
407
+ <CurrencySelect
408
+ mode="selected"
409
+ hasSelected={(currency) =>
410
+ currencies.fields.some((x: any) => x.currency_id === currency.id) ||
411
+ currency.id === defaultCurrencyId
418
412
  }
419
- setValue(getFieldName('currency'), findCurrency(settings.paymentMethods, currencyId), {
420
- shouldValidate: true,
421
- });
422
- setValue(getFieldName('currency_id'), currencyId, { shouldValidate: true });
423
- }}
424
- value={defaultCurrencyId}
425
- disabled={isLocked}
426
- selectSX={{ '.MuiOutlinedInput-notchedOutline': { border: 'none' } }}
427
- />
428
- </InputAdornment>
429
- ),
413
+ currencyFilter={(c) => c.type !== 'credit'}
414
+ onSelect={(currencyId) => {
415
+ const index = currencies.fields.findIndex(
416
+ (x: any) => x.currency_id === defaultCurrencyId
417
+ );
418
+ if (index > -1) {
419
+ // @ts-ignore
420
+ handleCurrencyChange(index, currencyId);
421
+ }
422
+ setValue(getFieldName('currency'), findCurrency(settings.paymentMethods, currencyId), {
423
+ shouldValidate: true,
424
+ });
425
+ setValue(getFieldName('currency_id'), currencyId, { shouldValidate: true });
426
+ }}
427
+ value={defaultCurrencyId}
428
+ disabled={isLocked}
429
+ selectSX={{ '.MuiOutlinedInput-notchedOutline': { border: 'none' } }}
430
+ />
431
+ </InputAdornment>
432
+ ),
433
+ },
430
434
  }}
431
435
  onChange={(e) => {
432
436
  const { value } = e.target;
@@ -480,31 +484,33 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
480
484
  sx={{ minWidth: '300px' }}
481
485
  error={!!getFieldError(fieldName)}
482
486
  helperText={getFieldError(fieldName)?.message as string}
483
- InputProps={{
484
- endAdornment: (
485
- <InputAdornment position="end">
486
- <CurrencySelect
487
- mode="selected"
488
- hasSelected={(c) =>
489
- currencies.fields.some((x: any) => x.currency_id === c.id) ||
490
- c.id === defaultCurrencyId
491
- }
492
- currencyFilter={(c) => c.type !== 'credit'}
493
- onSelect={(currencyId) => {
494
- const cIndex = currencies.fields.findIndex(
495
- (x: any) => x.currency_id === currency?.id
496
- );
497
- if (cIndex > -1) {
498
- // @ts-ignore
499
- handleCurrencyChange(cIndex, currencyId);
487
+ slotProps={{
488
+ input: {
489
+ endAdornment: (
490
+ <InputAdornment position="end">
491
+ <CurrencySelect
492
+ mode="selected"
493
+ hasSelected={(c) =>
494
+ currencies.fields.some((x: any) => x.currency_id === c.id) ||
495
+ c.id === defaultCurrencyId
500
496
  }
501
- }}
502
- value={currency?.id!}
503
- disabled={isLocked}
504
- selectSX={{ '.MuiOutlinedInput-notchedOutline': { border: 'none' } }}
505
- />
506
- </InputAdornment>
507
- ),
497
+ currencyFilter={(c) => c.type !== 'credit'}
498
+ onSelect={(currencyId) => {
499
+ const cIndex = currencies.fields.findIndex(
500
+ (x: any) => x.currency_id === currency?.id
501
+ );
502
+ if (cIndex > -1) {
503
+ // @ts-ignore
504
+ handleCurrencyChange(cIndex, currencyId);
505
+ }
506
+ }}
507
+ value={currency?.id!}
508
+ disabled={isLocked}
509
+ selectSX={{ '.MuiOutlinedInput-notchedOutline': { border: 'none' } }}
510
+ />
511
+ </InputAdornment>
512
+ ),
513
+ },
508
514
  }}
509
515
  />
510
516
  )}
@@ -569,9 +575,11 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
569
575
  type="number"
570
576
  size="small"
571
577
  fullWidth
572
- InputProps={{
573
- startAdornment: <InputAdornment position="start">{t('common.per')}</InputAdornment>,
574
- endAdornment: <InputAdornment position="end">{t('common.unit')}</InputAdornment>,
578
+ slotProps={{
579
+ input: {
580
+ startAdornment: <InputAdornment position="start">{t('common.per')}</InputAdornment>,
581
+ endAdornment: <InputAdornment position="end">{t('common.unit')}</InputAdornment>,
582
+ },
575
583
  }}
576
584
  />
577
585
  )}
@@ -1117,7 +1125,16 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
1117
1125
  render={({ field }) => (
1118
1126
  <Box sx={{ width: '100%', mb: 2 }}>
1119
1127
  <FormLabel>{t('admin.price.nickname.label')}</FormLabel>
1120
- <TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} inputProps={{ maxLength: 64 }} />
1128
+ <TextField
1129
+ {...field}
1130
+ size="small"
1131
+ sx={{ width: INPUT_WIDTH }}
1132
+ slotProps={{
1133
+ htmlInput: {
1134
+ maxLength: 64,
1135
+ },
1136
+ }}
1137
+ />
1121
1138
  </Box>
1122
1139
  )}
1123
1140
  />
@@ -1133,7 +1150,16 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
1133
1150
  render={({ field }) => (
1134
1151
  <Box sx={{ width: '100%', mb: 2 }}>
1135
1152
  <FormLabel>{t('admin.price.lookup_key.label')}</FormLabel>
1136
- <TextField {...field} size="small" sx={{ width: INPUT_WIDTH }} inputProps={{ maxLength: 64 }} />
1153
+ <TextField
1154
+ {...field}
1155
+ size="small"
1156
+ sx={{ width: INPUT_WIDTH }}
1157
+ slotProps={{
1158
+ htmlInput: {
1159
+ maxLength: 64,
1160
+ },
1161
+ }}
1162
+ />
1137
1163
  <Typography
1138
1164
  variant="caption"
1139
1165
  sx={{
@@ -89,7 +89,6 @@ export default function VendorConfig() {
89
89
  {error}
90
90
  </Alert>
91
91
  )}
92
-
93
92
  <Stack
94
93
  sx={{
95
94
  maxHeight: 400,
@@ -132,7 +131,11 @@ export default function VendorConfig() {
132
131
  noneSelectedVendors.map((vendor) => (
133
132
  <MenuItem key={vendor.id} value={vendor.id} sx={{ gap: 1 }}>
134
133
  <Typography variant="h5">{vendor.name}</Typography>
135
- <Typography variant="body2" color="text.secondary">
134
+ <Typography
135
+ variant="body2"
136
+ sx={{
137
+ color: 'text.secondary',
138
+ }}>
136
139
  {vendor.vendor_key}
137
140
  </Typography>
138
141
  </MenuItem>
@@ -213,7 +216,6 @@ export default function VendorConfig() {
213
216
  })}
214
217
  </Alert>
215
218
  )}
216
-
217
219
  <Divider />
218
220
  <Stack
219
221
  direction="row"