payment-kit 1.21.13 → 1.21.14

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 (55) hide show
  1. package/api/src/crons/payment-stat.ts +31 -23
  2. package/api/src/libs/invoice.ts +29 -4
  3. package/api/src/libs/product.ts +28 -4
  4. package/api/src/routes/checkout-sessions.ts +46 -1
  5. package/api/src/routes/index.ts +2 -0
  6. package/api/src/routes/invoices.ts +63 -2
  7. package/api/src/routes/payment-stats.ts +244 -22
  8. package/api/src/routes/products.ts +3 -0
  9. package/api/src/routes/tax-rates.ts +220 -0
  10. package/api/src/store/migrations/20251001-add-tax-code-to-products.ts +20 -0
  11. package/api/src/store/migrations/20251001-create-tax-rates.ts +17 -0
  12. package/api/src/store/migrations/20251007-relate-tax-rate-to-invoice.ts +24 -0
  13. package/api/src/store/migrations/20251009-add-tax-behavior.ts +21 -0
  14. package/api/src/store/models/index.ts +3 -0
  15. package/api/src/store/models/invoice-item.ts +10 -0
  16. package/api/src/store/models/price.ts +7 -0
  17. package/api/src/store/models/product.ts +7 -0
  18. package/api/src/store/models/tax-rate.ts +352 -0
  19. package/api/tests/models/tax-rate.spec.ts +777 -0
  20. package/blocklet.yml +2 -2
  21. package/package.json +6 -6
  22. package/public/currencies/dollar.png +0 -0
  23. package/src/components/collapse.tsx +3 -2
  24. package/src/components/drawer-form.tsx +2 -1
  25. package/src/components/invoice/list.tsx +38 -1
  26. package/src/components/invoice/table.tsx +48 -2
  27. package/src/components/metadata/form.tsx +2 -2
  28. package/src/components/payment-intent/list.tsx +19 -1
  29. package/src/components/payouts/list.tsx +19 -1
  30. package/src/components/price/currency-select.tsx +105 -48
  31. package/src/components/price/form.tsx +3 -1
  32. package/src/components/product/form.tsx +79 -5
  33. package/src/components/refund/list.tsx +20 -1
  34. package/src/components/subscription/items/actions.tsx +25 -15
  35. package/src/components/subscription/list.tsx +16 -1
  36. package/src/components/tax/actions.tsx +140 -0
  37. package/src/components/tax/filter-toolbar.tsx +230 -0
  38. package/src/components/tax/tax-code-select.tsx +633 -0
  39. package/src/components/tax/tax-rate-form.tsx +177 -0
  40. package/src/components/tax/tax-utils.ts +38 -0
  41. package/src/components/tax/taxCodes.json +10882 -0
  42. package/src/components/uploader.tsx +3 -0
  43. package/src/locales/en.tsx +152 -0
  44. package/src/locales/zh.tsx +149 -0
  45. package/src/pages/admin/billing/invoices/detail.tsx +1 -1
  46. package/src/pages/admin/index.tsx +2 -0
  47. package/src/pages/admin/overview.tsx +1114 -322
  48. package/src/pages/admin/products/vendors/index.tsx +4 -2
  49. package/src/pages/admin/tax/create.tsx +104 -0
  50. package/src/pages/admin/tax/detail.tsx +476 -0
  51. package/src/pages/admin/tax/edit.tsx +126 -0
  52. package/src/pages/admin/tax/index.tsx +86 -0
  53. package/src/pages/admin/tax/list.tsx +334 -0
  54. package/src/pages/customer/subscription/change-payment.tsx +1 -1
  55. package/src/pages/home.tsx +6 -3
@@ -223,8 +223,10 @@ export default function VendorsList() {
223
223
  <Typography
224
224
  key={`${info.label}-label`}
225
225
  variant="body2"
226
- color="text.secondary"
227
- sx={{ justifySelf: 'start' }}>
226
+ sx={{
227
+ color: 'text.secondary',
228
+ justifySelf: 'start',
229
+ }}>
228
230
  {info.label}:
229
231
  </Typography>
230
232
  <Box sx={{ display: 'flex', alignItems: 'center', overflow: 'hidden' }}>
@@ -0,0 +1,104 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import Toast from '@arcblock/ux/lib/Toast';
3
+ import { api, formatError } from '@blocklet/payment-react';
4
+ import { AddOutlined } from '@mui/icons-material';
5
+ import { Button, CircularProgress, Stack } from '@mui/material';
6
+ import { useState } from 'react';
7
+ import { FormProvider, useForm } from 'react-hook-form';
8
+ import { dispatch } from 'use-bus';
9
+
10
+ import DrawerForm from '../../../components/drawer-form';
11
+ import TaxRateForm, { TaxRateFormValues, generateTaxRateDisplayName } from '../../../components/tax/tax-rate-form';
12
+
13
+ type FormData = TaxRateFormValues;
14
+
15
+ const DEFAULT_VALUES: FormData = {
16
+ display_name: '',
17
+ description: '',
18
+ country: '',
19
+ state: '',
20
+ postal_code: '',
21
+ tax_code: '',
22
+ percentage: 0,
23
+ };
24
+
25
+ export default function TaxRateCreate() {
26
+ const { t, locale } = useLocaleContext();
27
+ const [loading, setLoading] = useState(false);
28
+
29
+ const methods = useForm<FormData>({
30
+ mode: 'onChange',
31
+ defaultValues: DEFAULT_VALUES,
32
+ });
33
+
34
+ const { handleSubmit, reset, formState } = methods;
35
+
36
+ const handleClose = () => {
37
+ reset(DEFAULT_VALUES);
38
+ dispatch('drawer.submitted');
39
+ };
40
+
41
+ const onSubmit = async (values: FormData) => {
42
+ try {
43
+ setLoading(true);
44
+ const { metadata, ...rest } = values;
45
+
46
+ const displayName =
47
+ rest.display_name ||
48
+ generateTaxRateDisplayName(
49
+ rest.country,
50
+ rest.state,
51
+ rest.postal_code,
52
+ Number(rest.percentage),
53
+ rest.tax_code,
54
+ locale
55
+ );
56
+
57
+ await api.post('/api/tax-rates', {
58
+ ...rest,
59
+ display_name: displayName,
60
+ active: true,
61
+ percentage: Number(rest.percentage),
62
+ metadata,
63
+ });
64
+
65
+ Toast.success(t('admin.taxRate.created'));
66
+ dispatch('tax-rate.created');
67
+ dispatch('drawer.submitted');
68
+ handleClose();
69
+ } catch (error) {
70
+ console.error(error);
71
+ Toast.error(formatError(error));
72
+ } finally {
73
+ setLoading(false);
74
+ }
75
+ };
76
+
77
+ return (
78
+ <DrawerForm
79
+ icon={<AddOutlined />}
80
+ text={t('admin.taxRate.create')}
81
+ onClose={handleClose}
82
+ hideLiveMode
83
+ width={720}
84
+ addons={
85
+ <Stack direction="row" spacing={1}>
86
+ <Button variant="text" color="inherit" onClick={handleClose}>
87
+ {t('common.cancel')}
88
+ </Button>
89
+ <Button
90
+ variant="contained"
91
+ color="primary"
92
+ onClick={handleSubmit(onSubmit)}
93
+ disabled={loading || !formState.isValid}>
94
+ {loading && <CircularProgress size={16} sx={{ mr: 1 }} />}
95
+ {t('admin.taxRate.createAction')}
96
+ </Button>
97
+ </Stack>
98
+ }>
99
+ <FormProvider {...methods}>
100
+ <TaxRateForm />
101
+ </FormProvider>
102
+ </DrawerForm>
103
+ );
104
+ }
@@ -0,0 +1,476 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import Toast from '@arcblock/ux/lib/Toast';
3
+ import { api, formatError, formatTime, useMobile } from '@blocklet/payment-react';
4
+ import { ArrowBackOutlined, EditOutlined } from '@mui/icons-material';
5
+ import { Alert, Box, Button, CircularProgress, Divider, Stack, Tooltip, Typography } from '@mui/material';
6
+ import { styled } from '@mui/system';
7
+ import { useRequest, useSetState } from 'ahooks';
8
+ import { useNavigate } from 'react-router-dom';
9
+ import { useForm, FormProvider } from 'react-hook-form';
10
+ import { dispatch } from 'use-bus';
11
+ import { FlagEmoji } from 'react-international-phone';
12
+
13
+ import Copyable from '../../../components/copyable';
14
+ import InfoMetric from '../../../components/info-metric';
15
+ import InfoRow from '../../../components/info-row';
16
+ import InfoRowGroup from '../../../components/info-row-group';
17
+ import InvoiceList from '../../../components/invoice/list';
18
+ import MetadataEditor from '../../../components/metadata/editor';
19
+ import MetadataList from '../../../components/metadata/list';
20
+ import SectionHeader from '../../../components/section/header';
21
+ import TaxRateActions from '../../../components/tax/actions';
22
+ import TaxRateForm, { TaxRateFormValues } from '../../../components/tax/tax-rate-form';
23
+ import DrawerForm from '../../../components/drawer-form';
24
+ import { goBackOrFallback } from '../../../libs/util';
25
+ import { getTaxCodeInfo, getCountryInfo } from '../../../components/tax/tax-utils';
26
+
27
+ const getTaxRate = (id: string) => api.get(`/api/tax-rates/${id}`).then((res) => res.data);
28
+
29
+ type FormData = TaxRateFormValues & {
30
+ metadata: Array<{ key: string; value: string }>;
31
+ };
32
+
33
+ const toMetadataArray = (metadata: Record<string, any> = {}) =>
34
+ Object.entries(metadata).map(([key, value]) => ({
35
+ key,
36
+ value: typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value),
37
+ }));
38
+
39
+ const toMetadataObject = (metadataArray: Array<{ key: string; value: string }>) =>
40
+ metadataArray.reduce((acc: Record<string, any>, item) => {
41
+ if (!item.key) return acc;
42
+ if (!item.value) return acc;
43
+ try {
44
+ acc[item.key] = JSON.parse(item.value);
45
+ } catch {
46
+ acc[item.key] = item.value;
47
+ }
48
+ return acc;
49
+ }, {});
50
+
51
+ type Props = {
52
+ id: string;
53
+ };
54
+
55
+ export default function TaxRateDetail({ id }: Props) {
56
+ const { t, locale } = useLocaleContext();
57
+ const { isMobile } = useMobile();
58
+ const navigate = useNavigate();
59
+
60
+ const [state, setState] = useSetState({
61
+ editing: {
62
+ metadata: false,
63
+ taxRate: false,
64
+ },
65
+ loading: {
66
+ metadata: false,
67
+ taxRate: false,
68
+ },
69
+ });
70
+
71
+ const methods = useForm<FormData>({
72
+ defaultValues: {
73
+ display_name: '',
74
+ description: '',
75
+ country: '',
76
+ state: '',
77
+ postal_code: '',
78
+ tax_code: '',
79
+ percentage: 0,
80
+ metadata: [{ key: '', value: '' }],
81
+ },
82
+ });
83
+
84
+ const { data, loading, error, runAsync } = useRequest(() => getTaxRate(id));
85
+
86
+ if (loading && !data) {
87
+ return <CircularProgress />;
88
+ }
89
+
90
+ if (error) {
91
+ return <Alert severity="error">{error.message}</Alert>;
92
+ }
93
+
94
+ if (!data) {
95
+ return null;
96
+ }
97
+
98
+ const onChange = (action?: string) => {
99
+ if (action === 'delete') {
100
+ navigate('/admin/tax/rates');
101
+ } else {
102
+ runAsync();
103
+ }
104
+ };
105
+
106
+ const handleEditMetadata = () => {
107
+ setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
108
+ };
109
+
110
+ const handleEditTaxRate = () => {
111
+ methods.reset({
112
+ display_name: data.display_name,
113
+ description: data.description || '',
114
+ country: (data.country || '').toLowerCase(),
115
+ state: data.state || '',
116
+ postal_code: data.postal_code || '',
117
+ tax_code: data.tax_code || '',
118
+ percentage: data.percentage,
119
+ metadata: toMetadataArray(data.metadata || {}),
120
+ });
121
+ setState((prev) => ({ editing: { ...prev.editing, taxRate: true } }));
122
+ };
123
+
124
+ const onUpdateMetadata = async (updates: any) => {
125
+ try {
126
+ setState((prev) => ({ loading: { ...prev.loading, metadata: true } }));
127
+ await api.put(`/api/tax-rates/${id}`, updates);
128
+ Toast.success(t('common.saved'));
129
+ dispatch('tax-rate.updated');
130
+ runAsync();
131
+ } catch (err) {
132
+ console.error(err);
133
+ Toast.error(formatError(err));
134
+ } finally {
135
+ setState((prev) => ({ loading: { ...prev.loading, metadata: false } }));
136
+ }
137
+ };
138
+
139
+ const onUpdateTaxRate = async (formData: FormData) => {
140
+ try {
141
+ setState((prev) => ({ loading: { ...prev.loading, taxRate: true } }));
142
+ const { metadata, ...rest } = formData;
143
+ await api.put(`/api/tax-rates/${id}`, {
144
+ ...rest,
145
+ percentage: Number(rest.percentage),
146
+ metadata: toMetadataObject(metadata || []),
147
+ });
148
+ Toast.success(t('admin.taxRate.updated'));
149
+ dispatch('tax-rate.updated');
150
+ setState((prev) => ({ editing: { ...prev.editing, taxRate: false } }));
151
+ runAsync();
152
+ } catch (err) {
153
+ console.error(err);
154
+ Toast.error(formatError(err));
155
+ } finally {
156
+ setState((prev) => ({ loading: { ...prev.loading, taxRate: false } }));
157
+ }
158
+ };
159
+
160
+ const countryInfo = data.country ? getCountryInfo(data.country) : null;
161
+ const locationParts = [data.state, data.postal_code].filter(Boolean);
162
+ const location = countryInfo
163
+ ? `${countryInfo.name}${locationParts.length > 0 ? ` · ${locationParts.join(' · ')}` : ''}`
164
+ : '—';
165
+
166
+ const taxCodeInfo = data.tax_code ? getTaxCodeInfo(data.tax_code, locale) : null;
167
+
168
+ return (
169
+ <Root direction="column" spacing={2.5} sx={{ mb: 4 }}>
170
+ <Box>
171
+ {!data.active && (
172
+ <Alert severity="warning" sx={{ mb: 2 }}>
173
+ {t('admin.taxRate.inactiveWarning')}
174
+ </Alert>
175
+ )}
176
+ <Stack
177
+ className="page-header"
178
+ direction="row"
179
+ sx={{
180
+ justifyContent: 'space-between',
181
+ alignItems: 'center',
182
+ }}>
183
+ <Stack
184
+ direction="row"
185
+ onClick={() => goBackOrFallback('/admin/tax/rates')}
186
+ sx={{
187
+ alignItems: 'center',
188
+ fontWeight: 'normal',
189
+ cursor: 'pointer',
190
+ }}>
191
+ <ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
192
+ <Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
193
+ {t('admin.taxRates')}
194
+ </Typography>
195
+ </Stack>
196
+ <TaxRateActions data={data} onChange={onChange} />
197
+ </Stack>
198
+ <Box
199
+ sx={{
200
+ mt: 4,
201
+ mb: 3,
202
+ display: 'flex',
203
+ gap: {
204
+ xs: 2,
205
+ sm: 2,
206
+ md: 5,
207
+ },
208
+ flexWrap: 'wrap',
209
+ flexDirection: {
210
+ xs: 'column',
211
+ sm: 'column',
212
+ md: 'row',
213
+ },
214
+ alignItems: {
215
+ xs: 'flex-start',
216
+ sm: 'flex-start',
217
+ md: 'center',
218
+ },
219
+ }}>
220
+ <Stack
221
+ direction="row"
222
+ sx={{
223
+ justifyContent: 'space-between',
224
+ alignItems: 'center',
225
+ }}>
226
+ <Stack
227
+ direction="column"
228
+ sx={{
229
+ alignItems: 'flex-start',
230
+ justifyContent: 'space-around',
231
+ }}>
232
+ <Typography
233
+ variant="h2"
234
+ sx={{
235
+ color: 'text.primary',
236
+ }}>
237
+ {data.display_name}
238
+ </Typography>
239
+ {data.description && (
240
+ <Typography
241
+ variant="subtitle1"
242
+ sx={{
243
+ color: 'text.lighter',
244
+ }}>
245
+ {data.description}
246
+ </Typography>
247
+ )}
248
+ </Stack>
249
+ </Stack>
250
+ <Stack
251
+ className="section-body"
252
+ sx={{
253
+ justifyContent: 'flex-start',
254
+ flexWrap: 'wrap',
255
+ 'hr.MuiDivider-root:last-child': {
256
+ display: 'none',
257
+ },
258
+ flexDirection: {
259
+ xs: 'column',
260
+ sm: 'column',
261
+ md: 'row',
262
+ },
263
+ alignItems: 'flex-start',
264
+ gap: {
265
+ xs: 1,
266
+ sm: 1,
267
+ md: 3,
268
+ },
269
+ }}>
270
+ <InfoMetric label={t('common.id')} value={<Copyable text={id} style={{ marginLeft: 4 }} />} divider />
271
+ <InfoMetric label={t('admin.taxRate.percentage')} value={`${data.percentage}%`} divider />
272
+ <InfoMetric
273
+ label={t('admin.taxRate.location')}
274
+ value={
275
+ countryInfo ? (
276
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
277
+ <FlagEmoji iso2={data.country.toLowerCase()} style={{ width: 24 }} />
278
+ <Typography variant="body2">{location}</Typography>
279
+ </Box>
280
+ ) : (
281
+ location
282
+ )
283
+ }
284
+ divider
285
+ />
286
+ <InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
287
+ </Stack>
288
+ </Box>
289
+ <Divider />
290
+ </Box>
291
+ <Stack
292
+ sx={{
293
+ flexDirection: {
294
+ xs: 'column',
295
+ lg: 'row',
296
+ },
297
+ gap: {
298
+ xs: 2.5,
299
+ md: 4,
300
+ },
301
+ '.tax-column-1': {
302
+ minWidth: {
303
+ xs: '100%',
304
+ lg: '600px',
305
+ },
306
+ },
307
+ '.tax-column-2': {
308
+ width: {
309
+ xs: '100%',
310
+ md: '100%',
311
+ lg: '320px',
312
+ },
313
+ maxWidth: {
314
+ xs: '100%',
315
+ md: '33%',
316
+ },
317
+ },
318
+ }}>
319
+ <Box
320
+ className="tax-column-1"
321
+ sx={{
322
+ flex: 1,
323
+ gap: 2.5,
324
+ display: 'flex',
325
+ flexDirection: 'column',
326
+ }}>
327
+ <Box className="section" sx={{ containerType: 'inline-size' }}>
328
+ <SectionHeader title={t('admin.details')}>
329
+ <Button
330
+ variant="text"
331
+ color="inherit"
332
+ size="small"
333
+ sx={{ color: 'text.link' }}
334
+ disabled={state.editing.taxRate}
335
+ onClick={handleEditTaxRate}>
336
+ {t('common.edit')}
337
+ </Button>
338
+ </SectionHeader>
339
+ <InfoRowGroup
340
+ sx={{
341
+ display: 'grid',
342
+ gridTemplateColumns: {
343
+ xs: 'repeat(1, 1fr)',
344
+ xl: 'repeat(2, 1fr)',
345
+ },
346
+ '@container (min-width: 1000px)': {
347
+ gridTemplateColumns: 'repeat(2, 1fr)',
348
+ },
349
+ '.info-row-wrapper': {
350
+ gap: 1,
351
+ flexDirection: {
352
+ xs: 'column',
353
+ xl: 'row',
354
+ },
355
+ alignItems: {
356
+ xs: 'flex-start',
357
+ xl: 'center',
358
+ },
359
+ '@container (min-width: 1000px)': {
360
+ flexDirection: 'row',
361
+ alignItems: 'center',
362
+ },
363
+ },
364
+ }}>
365
+ <InfoRow label={t('admin.taxRate.displayName')} value={data.display_name} />
366
+ <InfoRow
367
+ label={t('admin.taxRate.country')}
368
+ value={
369
+ countryInfo ? (
370
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
371
+ <FlagEmoji iso2={data.country.toLowerCase()} style={{ width: 24 }} />
372
+ <Typography variant="body2">{countryInfo.name}</Typography>
373
+ </Box>
374
+ ) : (
375
+ '—'
376
+ )
377
+ }
378
+ />
379
+ <InfoRow label={t('admin.taxRate.state')} value={data.state || '—'} />
380
+ <InfoRow label={t('admin.taxRate.postalCode')} value={data.postal_code || '—'} />
381
+ <InfoRow label={t('admin.taxRate.percentage')} value={`${data.percentage}%`} />
382
+ <InfoRow
383
+ label={t('admin.taxRate.taxCode')}
384
+ value={
385
+ taxCodeInfo ? (
386
+ <Tooltip title={taxCodeInfo.description} arrow placement="top">
387
+ <Box>
388
+ <Typography variant="body2" sx={{ fontWeight: 500 }}>
389
+ {taxCodeInfo.name}
390
+ </Typography>
391
+ <Typography
392
+ variant="caption"
393
+ sx={{
394
+ color: 'text.secondary',
395
+ }}>
396
+ {taxCodeInfo.id}
397
+ </Typography>
398
+ </Box>
399
+ </Tooltip>
400
+ ) : (
401
+ data.tax_code || '—'
402
+ )
403
+ }
404
+ />
405
+ <InfoRow label={t('common.status')} value={data.active ? t('common.active') : t('common.inactive')} />
406
+ <InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
407
+ </InfoRowGroup>
408
+ </Box>
409
+ <Divider />
410
+
411
+ <Box className="section">
412
+ <SectionHeader title={t('admin.taxRate.associatedInvoices')} />
413
+ <Box className="section-body">
414
+ <InvoiceList
415
+ features={{ customer: true, filter: false, toolbar: false }}
416
+ tax_rate_id={id}
417
+ mode="admin"
418
+ status=""
419
+ />
420
+ </Box>
421
+ </Box>
422
+ </Box>
423
+ {isMobile && <Divider />}
424
+ <Box className="tax-column-2" sx={{ gap: 2.5, display: 'flex', flexDirection: 'column' }}>
425
+ <Box className="section">
426
+ <SectionHeader title={t('common.metadata.label')}>
427
+ <Button
428
+ variant="text"
429
+ color="inherit"
430
+ size="small"
431
+ sx={{ color: 'text.link' }}
432
+ disabled={state.editing.metadata}
433
+ onClick={handleEditMetadata}>
434
+ {t('common.edit')}
435
+ </Button>
436
+ </SectionHeader>
437
+ <Box className="section-body">
438
+ <MetadataList data={data.metadata || {}} handleEditMetadata={handleEditMetadata} />
439
+ {state.editing.metadata && (
440
+ <MetadataEditor
441
+ data={data}
442
+ loading={state.loading.metadata}
443
+ onSave={onUpdateMetadata}
444
+ onCancel={() => setState((prev) => ({ editing: { ...prev.editing, metadata: false } }))}
445
+ />
446
+ )}
447
+ </Box>
448
+ </Box>
449
+ </Box>
450
+ </Stack>
451
+ {state.editing.taxRate && (
452
+ <DrawerForm
453
+ icon={<EditOutlined />}
454
+ text={t('admin.taxRate.edit')}
455
+ open={state.editing.taxRate}
456
+ onClose={() => setState((prev) => ({ editing: { ...prev.editing, taxRate: false } }))}
457
+ width={640}
458
+ addons={
459
+ <Button
460
+ variant="contained"
461
+ size="small"
462
+ disabled={state.loading.taxRate}
463
+ onClick={methods.handleSubmit(onUpdateTaxRate)}>
464
+ {t('common.save')}
465
+ </Button>
466
+ }>
467
+ <FormProvider {...methods}>
468
+ <TaxRateForm />
469
+ </FormProvider>
470
+ </DrawerForm>
471
+ )}
472
+ </Root>
473
+ );
474
+ }
475
+
476
+ const Root = styled(Stack)``;