@vendure/dashboard 3.5.0-minor-202510071456 → 3.5.0-minor-202510161257
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/dist/plugin/dashboard.plugin.js +1 -1
- package/dist/vite/utils/ast-utils.spec.js +3 -3
- package/dist/vite/vite-plugin-hmr.d.ts +8 -0
- package/dist/vite/vite-plugin-hmr.js +34 -0
- package/dist/vite/vite-plugin-theme.js +6 -6
- package/dist/vite/vite-plugin-transform-index.js +6 -1
- package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
- package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
- package/package.json +17 -5
- package/src/app/app-providers.tsx +4 -1
- package/src/app/common/map-faceted-filter-fields.ts +21 -0
- package/src/app/main.tsx +3 -1
- package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
- package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
- package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
- package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
- package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
- package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
- package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
- package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
- package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +1 -1
- package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
- package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
- package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
- package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
- package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
- package/src/app/routes/_authenticated/_products/products.tsx +6 -2
- package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
- package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
- package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -2
- package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
- package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
- package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
- package/src/app/routes/login.tsx +2 -2
- package/src/i18n/locales/ar.po +420 -289
- package/src/i18n/locales/cs.po +420 -289
- package/src/i18n/locales/de.po +420 -289
- package/src/i18n/locales/en.po +420 -289
- package/src/i18n/locales/es.po +420 -289
- package/src/i18n/locales/fa.po +420 -289
- package/src/i18n/locales/fr.po +468 -337
- package/src/i18n/locales/he.po +420 -289
- package/src/i18n/locales/hr.po +420 -289
- package/src/i18n/locales/it.po +420 -289
- package/src/i18n/locales/ja.po +420 -289
- package/src/i18n/locales/nb.po +420 -289
- package/src/i18n/locales/ne.po +420 -289
- package/src/i18n/locales/pl.po +420 -289
- package/src/i18n/locales/pt_BR.po +420 -289
- package/src/i18n/locales/pt_PT.po +420 -289
- package/src/i18n/locales/ru.po +420 -289
- package/src/i18n/locales/sv.po +420 -289
- package/src/i18n/locales/tr.po +420 -289
- package/src/i18n/locales/uk.po +420 -289
- package/src/i18n/locales/zh_Hans.po +420 -289
- package/src/i18n/locales/zh_Hant.po +420 -289
- package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
- package/src/lib/components/data-input/affixed-input.tsx +5 -2
- package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
- package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
- package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
- package/src/lib/components/data-input/datetime-input.tsx +27 -13
- package/src/lib/components/data-input/default-relation-input.tsx +18 -12
- package/src/lib/components/data-input/money-input.stories.tsx +88 -0
- package/src/lib/components/data-input/number-input.stories.tsx +103 -0
- package/src/lib/components/data-input/number-input.tsx +10 -4
- package/src/lib/components/data-input/password-input.stories.tsx +65 -0
- package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
- package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
- package/src/lib/components/data-input/slug-input.tsx +9 -10
- package/src/lib/components/data-input/text-input.stories.tsx +52 -0
- package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
- package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
- package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
- package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
- package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
- package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
- package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
- package/src/lib/components/data-table/data-table-filter-badge.tsx +23 -16
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
- package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
- package/src/lib/components/data-table/data-table.stories.tsx +249 -0
- package/src/lib/components/data-table/data-table.tsx +37 -9
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
- package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
- package/src/lib/components/layout/nav-user.tsx +19 -13
- package/src/lib/components/login/login-form.tsx +39 -123
- package/src/lib/components/shared/alerts.tsx +29 -17
- package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
- package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
- package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
- package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
- package/src/lib/components/shared/customer-group-selector.tsx +5 -2
- package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
- package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
- package/src/lib/components/shared/facet-value-selector.tsx +130 -34
- package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
- package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
- package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
- package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
- package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
- package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
- package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
- package/src/lib/components/shared/vendure-image.tsx +6 -7
- package/src/lib/components/ui/accordion.stories.tsx +33 -0
- package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
- package/src/lib/components/ui/alert.stories.tsx +35 -0
- package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
- package/src/lib/components/ui/badge.stories.tsx +28 -0
- package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
- package/src/lib/components/ui/button.stories.tsx +38 -0
- package/src/lib/components/ui/calendar.stories.tsx +22 -0
- package/src/lib/components/ui/card.stories.tsx +28 -0
- package/src/lib/components/ui/carousel.stories.tsx +34 -0
- package/src/lib/components/ui/checkbox.stories.tsx +31 -0
- package/src/lib/components/ui/collapsible.stories.tsx +39 -0
- package/src/lib/components/ui/command.stories.tsx +44 -0
- package/src/lib/components/ui/context-menu.stories.tsx +38 -0
- package/src/lib/components/ui/dialog.stories.tsx +52 -0
- package/src/lib/components/ui/drawer.stories.tsx +50 -0
- package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
- package/src/lib/components/ui/hover-card.stories.tsx +38 -0
- package/src/lib/components/ui/input-group.tsx +148 -0
- package/src/lib/components/ui/input-otp.stories.tsx +30 -0
- package/src/lib/components/ui/input.stories.tsx +38 -0
- package/src/lib/components/ui/label.stories.tsx +24 -0
- package/src/lib/components/ui/menubar.stories.tsx +53 -0
- package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
- package/src/lib/components/ui/pagination.stories.tsx +51 -0
- package/src/lib/components/ui/password-input.stories.tsx +32 -0
- package/src/lib/components/ui/password-input.tsx +33 -0
- package/src/lib/components/ui/popover.stories.tsx +33 -0
- package/src/lib/components/ui/progress.stories.tsx +27 -0
- package/src/lib/components/ui/radio-group.stories.tsx +34 -0
- package/src/lib/components/ui/resizable.stories.tsx +32 -0
- package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
- package/src/lib/components/ui/select.stories.tsx +36 -0
- package/src/lib/components/ui/separator.stories.tsx +35 -0
- package/src/lib/components/ui/sheet.stories.tsx +50 -0
- package/src/lib/components/ui/sidebar-context.ts +16 -0
- package/src/lib/components/ui/sidebar.tsx +2 -13
- package/src/lib/components/ui/skeleton.stories.tsx +26 -0
- package/src/lib/components/ui/slider.stories.tsx +37 -0
- package/src/lib/components/ui/switch.stories.tsx +31 -0
- package/src/lib/components/ui/table.stories.tsx +52 -0
- package/src/lib/components/ui/tabs.stories.tsx +29 -0
- package/src/lib/components/ui/textarea.stories.tsx +32 -0
- package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
- package/src/lib/components/ui/toggle.stories.tsx +39 -0
- package/src/lib/components/ui/tooltip.stories.tsx +30 -0
- package/src/lib/components/ui/tooltip.tsx +2 -2
- package/src/lib/framework/alert/alert-extensions.tsx +0 -11
- package/src/lib/framework/alert/alert-item.tsx +14 -19
- package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
- package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
- package/src/lib/framework/component-registry/component-registry.tsx +3 -14
- package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
- package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
- package/src/lib/framework/defaults.ts +9 -13
- package/src/lib/framework/extension-api/input-component-extensions.tsx +6 -1
- package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
- package/src/lib/framework/extension-api/types/alerts.ts +12 -6
- package/src/lib/framework/extension-api/types/data-table.ts +5 -2
- package/src/lib/framework/extension-api/types/login.ts +0 -21
- package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
- package/src/lib/framework/layout-engine/page-layout.tsx +11 -9
- package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
- package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
- package/src/lib/framework/page/detail-page.stories.tsx +151 -0
- package/src/lib/framework/page/list-page.stories.tsx +217 -0
- package/src/lib/framework/page/list-page.tsx +8 -1
- package/src/lib/graphql/api.ts +18 -1
- package/src/lib/graphql/graphql-env.d.ts +1 -1
- package/src/lib/hooks/use-alerts.ts +84 -0
- package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
- package/src/lib/index.ts +12 -5
- package/src/lib/providers/alerts-provider.tsx +60 -0
- package/src/lib/providers/theme-provider.tsx +6 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Mail } from 'lucide-react';
|
|
3
|
+
import { useForm } from 'react-hook-form';
|
|
4
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
5
|
+
import { AffixedInput } from './affixed-input.js';
|
|
6
|
+
|
|
7
|
+
const meta = {
|
|
8
|
+
title: 'Form Inputs/AffixedInput',
|
|
9
|
+
component: AffixedInput,
|
|
10
|
+
...withDescription(import.meta.url, './affixed-input.js'),
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered',
|
|
13
|
+
},
|
|
14
|
+
tags: ['autodocs'],
|
|
15
|
+
argTypes: {
|
|
16
|
+
prefix: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'Content to display before the input',
|
|
19
|
+
},
|
|
20
|
+
suffix: {
|
|
21
|
+
control: 'text',
|
|
22
|
+
description: 'Content to display after the input',
|
|
23
|
+
},
|
|
24
|
+
type: {
|
|
25
|
+
control: 'select',
|
|
26
|
+
options: ['text', 'number', 'email', 'url', 'tel'],
|
|
27
|
+
description: 'Input type',
|
|
28
|
+
},
|
|
29
|
+
placeholder: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
description: 'Placeholder text',
|
|
32
|
+
},
|
|
33
|
+
disabled: {
|
|
34
|
+
control: 'boolean',
|
|
35
|
+
description: 'Whether the input is disabled',
|
|
36
|
+
},
|
|
37
|
+
value: {
|
|
38
|
+
control: 'text',
|
|
39
|
+
description: 'Input value',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
} satisfies Meta<typeof AffixedInput>;
|
|
43
|
+
|
|
44
|
+
export default meta;
|
|
45
|
+
type Story = StoryObj<typeof meta>;
|
|
46
|
+
|
|
47
|
+
export const Playground: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
prefix: '$',
|
|
50
|
+
suffix: 'USD',
|
|
51
|
+
value: '100.00',
|
|
52
|
+
type: 'text',
|
|
53
|
+
placeholder: 'Enter amount',
|
|
54
|
+
disabled: false,
|
|
55
|
+
},
|
|
56
|
+
render: args => {
|
|
57
|
+
const { register } = useForm();
|
|
58
|
+
const field = register('amount');
|
|
59
|
+
return <AffixedInput {...field} {...args} />;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const WithIconPrefix: Story = {
|
|
64
|
+
render: () => {
|
|
65
|
+
const { register } = useForm();
|
|
66
|
+
const field = register('email');
|
|
67
|
+
return <AffixedInput {...field} prefix={<Mail className="w-4 h-4" />} placeholder="Enter email" />;
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const WithSuffix: Story = {
|
|
72
|
+
render: () => {
|
|
73
|
+
const { register } = useForm();
|
|
74
|
+
const field = register('percentage');
|
|
75
|
+
return <AffixedInput {...field} type="number" suffix="%" placeholder="Enter percentage" />;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Disabled: Story = {
|
|
80
|
+
render: () => {
|
|
81
|
+
const { register } = useForm();
|
|
82
|
+
const field = register('website');
|
|
83
|
+
return <AffixedInput {...field} prefix="https://" suffix=".com" disabled />;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const NumberWithSteps: Story = {
|
|
88
|
+
render: () => {
|
|
89
|
+
const { register } = useForm();
|
|
90
|
+
const field = register('weight');
|
|
91
|
+
return <AffixedInput {...field} type="number" suffix="kg" step="0.1" min="0" max="100" />;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -13,7 +13,7 @@ export type AffixedInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>
|
|
|
13
13
|
/**
|
|
14
14
|
* @description
|
|
15
15
|
* A component for displaying an input with a prefix and/or a suffix.
|
|
16
|
-
*
|
|
16
|
+
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```tsx
|
|
19
19
|
* <AffixedInput
|
|
@@ -24,7 +24,7 @@ export type AffixedInputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>
|
|
|
24
24
|
* onChange={e => field.onChange(e.target.valueAsNumber)}
|
|
25
25
|
* />
|
|
26
26
|
* ```
|
|
27
|
-
*
|
|
27
|
+
*
|
|
28
28
|
* @docsCategory form-components
|
|
29
29
|
* @docsPage AffixedInput
|
|
30
30
|
*/
|
|
@@ -65,6 +65,9 @@ export function AffixedInput({ prefix, suffix, className = '', ...props }: Reado
|
|
|
65
65
|
className={className}
|
|
66
66
|
style={style}
|
|
67
67
|
disabled={readOnly}
|
|
68
|
+
min={props.min}
|
|
69
|
+
max={props.max}
|
|
70
|
+
step={props.step}
|
|
68
71
|
/>
|
|
69
72
|
{suffix && (
|
|
70
73
|
<span ref={suffixRef} className="absolute right-3 text-muted-foreground whitespace-nowrap">
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
4
|
+
import { BooleanInput } from './boolean-input.js';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Form Inputs/BooleanInput',
|
|
8
|
+
component: BooleanInput,
|
|
9
|
+
...withDescription(import.meta.url, './boolean-input.js'),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'boolean',
|
|
17
|
+
description: 'Whether the switch is on',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof BooleanInput>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const Playground: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
value: false,
|
|
28
|
+
},
|
|
29
|
+
render: args => {
|
|
30
|
+
const { register } = useForm();
|
|
31
|
+
const field = register('playground');
|
|
32
|
+
return (
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
<BooleanInput {...field} {...args} />
|
|
35
|
+
<label className="text-sm font-medium">Enable notifications</label>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const ProductSettings: Story = {
|
|
42
|
+
render: () => {
|
|
43
|
+
const { register } = useForm();
|
|
44
|
+
return (
|
|
45
|
+
<div className="w-[350px] space-y-3">
|
|
46
|
+
<div className="flex items-center justify-between">
|
|
47
|
+
<div>
|
|
48
|
+
<label className="text-sm font-medium">Product enabled</label>
|
|
49
|
+
<p className="text-xs text-muted-foreground">
|
|
50
|
+
Make this product visible in the catalog
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
<BooleanInput {...register('enabled')} />
|
|
54
|
+
</div>
|
|
55
|
+
<div className="flex items-center justify-between">
|
|
56
|
+
<div>
|
|
57
|
+
<label className="text-sm font-medium">Featured</label>
|
|
58
|
+
<p className="text-xs text-muted-foreground">Show on homepage</p>
|
|
59
|
+
</div>
|
|
60
|
+
<BooleanInput {...register('featured')} />
|
|
61
|
+
</div>
|
|
62
|
+
<div className="flex items-center justify-between">
|
|
63
|
+
<div>
|
|
64
|
+
<label className="text-sm font-medium">Track inventory</label>
|
|
65
|
+
<p className="text-xs text-muted-foreground">Monitor stock levels</p>
|
|
66
|
+
</div>
|
|
67
|
+
<BooleanInput {...register('trackInventory')} />
|
|
68
|
+
</div>
|
|
69
|
+
<div className="flex items-center justify-between">
|
|
70
|
+
<div>
|
|
71
|
+
<label className="text-sm font-medium">Allow backorder</label>
|
|
72
|
+
<p className="text-xs text-muted-foreground">Accept orders when out of stock</p>
|
|
73
|
+
</div>
|
|
74
|
+
<BooleanInput {...register('allowBackorder')} />
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const StringValues: Story = {
|
|
82
|
+
render: () => {
|
|
83
|
+
const { register } = useForm();
|
|
84
|
+
return (
|
|
85
|
+
<div className="space-y-4">
|
|
86
|
+
<div className="flex items-center gap-2">
|
|
87
|
+
<BooleanInput {...register('trueValue')} />
|
|
88
|
+
<label className="text-sm font-medium">String value: "true"</label>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div className="flex items-center gap-2">
|
|
92
|
+
<BooleanInput {...register('falseValue')} />
|
|
93
|
+
<label className="text-sm font-medium">String value: "false"</label>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="text-sm text-muted-foreground">
|
|
97
|
+
Demonstrates handling of string "true"/"false" values
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
4
|
+
import { CheckboxInput } from './checkbox-input.js';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Form Inputs/CheckboxInput',
|
|
8
|
+
component: CheckboxInput,
|
|
9
|
+
...withDescription(import.meta.url, './checkbox-input.js'),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'boolean',
|
|
17
|
+
description: 'Whether the checkbox is checked',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof CheckboxInput>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const Playground: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
value: false,
|
|
28
|
+
},
|
|
29
|
+
render: args => {
|
|
30
|
+
const { register } = useForm();
|
|
31
|
+
const field = register('playground');
|
|
32
|
+
return (
|
|
33
|
+
<div className="flex items-center gap-2">
|
|
34
|
+
<CheckboxInput {...field} {...args} />
|
|
35
|
+
<label className="text-sm font-medium">Accept terms and conditions</label>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const MultipleCheckboxes: Story = {
|
|
42
|
+
render: () => {
|
|
43
|
+
const { register } = useForm();
|
|
44
|
+
return (
|
|
45
|
+
<div className="space-y-3">
|
|
46
|
+
<div className="flex items-center gap-2">
|
|
47
|
+
<CheckboxInput {...register('notifications')} />
|
|
48
|
+
<label className="text-sm font-medium">Email notifications</label>
|
|
49
|
+
</div>
|
|
50
|
+
<div className="flex items-center gap-2">
|
|
51
|
+
<CheckboxInput {...register('marketing')} />
|
|
52
|
+
<label className="text-sm font-medium">Marketing emails</label>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="flex items-center gap-2">
|
|
55
|
+
<CheckboxInput {...register('updates')} />
|
|
56
|
+
<label className="text-sm font-medium">Product updates</label>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
4
|
+
import { DateTimeInput } from './datetime-input.js';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Form Inputs/DateTimeInput',
|
|
8
|
+
component: DateTimeInput,
|
|
9
|
+
...withDescription(import.meta.url, './datetime-input.js'),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'text',
|
|
17
|
+
description: 'ISO 8601 datetime string',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof DateTimeInput>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const Playground: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
value: new Date('2024-06-15T14:30:00').toISOString(),
|
|
28
|
+
},
|
|
29
|
+
render: args => {
|
|
30
|
+
const { register } = useForm();
|
|
31
|
+
const field = register('playground');
|
|
32
|
+
return (
|
|
33
|
+
<div className="w-[400px]">
|
|
34
|
+
<DateTimeInput {...field} {...args} />
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const FutureDate: Story = {
|
|
41
|
+
render: () => {
|
|
42
|
+
const { register } = useForm();
|
|
43
|
+
const field = register('future');
|
|
44
|
+
return (
|
|
45
|
+
<div className="w-[400px]">
|
|
46
|
+
<DateTimeInput {...field} />
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const ClearableValue: Story = {
|
|
53
|
+
render: () => {
|
|
54
|
+
const { register } = useForm();
|
|
55
|
+
const field = register('clearable');
|
|
56
|
+
return (
|
|
57
|
+
<div className="w-[400px]">
|
|
58
|
+
<DateTimeInput {...field} />
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -13,7 +13,7 @@ import { isReadonlyField } from '@/vdb/framework/form-engine/utils.js';
|
|
|
13
13
|
import { useDisplayLocale } from '@/vdb/hooks/use-display-locale.js';
|
|
14
14
|
import { cn } from '@/vdb/lib/utils.js';
|
|
15
15
|
import type { Locale } from 'date-fns/locale';
|
|
16
|
-
import { CalendarClock } from 'lucide-react';
|
|
16
|
+
import { CalendarClock, X } from 'lucide-react';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @description
|
|
@@ -62,24 +62,38 @@ export function DateTimeInput({ value, onChange, fieldDef }: Readonly<DashboardF
|
|
|
62
62
|
const currentHours = newDate.getHours();
|
|
63
63
|
newDate.setHours(value === 'PM' ? currentHours + 12 : currentHours - 12);
|
|
64
64
|
}
|
|
65
|
-
onChange(newDate);
|
|
65
|
+
onChange(newDate.toISOString());
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
70
|
<Popover open={isOpen} onOpenChange={readOnly ? undefined : setIsOpen}>
|
|
71
71
|
<PopoverTrigger asChild>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
<div className="flex items-center">
|
|
73
|
+
<Button
|
|
74
|
+
variant="outline"
|
|
75
|
+
disabled={readOnly}
|
|
76
|
+
className={cn(
|
|
77
|
+
'w-full justify-start text-left font-normal shadow-xs',
|
|
78
|
+
date ? 'rounded-r-none' : 'text-muted-foreground',
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<CalendarClock className="mr-2 h-4 w-4" />
|
|
82
|
+
{date ? format(date, 'MM/dd/yyyy hh:mm aa') : <span>MM/DD/YYYY hh:mm aa</span>}
|
|
83
|
+
</Button>
|
|
84
|
+
{date ? (
|
|
85
|
+
<Button
|
|
86
|
+
variant="outline"
|
|
87
|
+
className="rounded-l-none border-l-0"
|
|
88
|
+
onClick={e => {
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
onChange(null);
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<X />
|
|
94
|
+
</Button>
|
|
95
|
+
) : null}
|
|
96
|
+
</div>
|
|
83
97
|
</PopoverTrigger>
|
|
84
98
|
<PopoverContent className="w-auto p-0">
|
|
85
99
|
<div className="sm:flex">
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { graphql } from '@/vdb/graphql/graphql.js';
|
|
2
2
|
import { Trans, useLingui } from '@lingui/react/macro';
|
|
3
|
-
import {
|
|
4
|
-
import { ControllerRenderProps } from 'react-hook-form';
|
|
3
|
+
import { useMemo } from 'react';
|
|
5
4
|
import { MultiRelationInput, SingleRelationInput } from './relation-input.js';
|
|
6
5
|
import { createRelationSelectorConfig } from './relation-selector.js';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DashboardFormComponentMetadata,
|
|
9
|
+
DashboardFormComponentProps,
|
|
10
|
+
RelationCustomFieldConfig,
|
|
11
|
+
} from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
9
12
|
import { isRelationCustomFieldConfig } from '@/vdb/framework/form-engine/utils.js';
|
|
10
13
|
|
|
11
14
|
interface PlaceholderIconProps {
|
|
@@ -549,11 +552,9 @@ const createEntityConfigs = (i18n: any) => ({
|
|
|
549
552
|
}),
|
|
550
553
|
});
|
|
551
554
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
disabled?: boolean;
|
|
556
|
-
}
|
|
555
|
+
type DefaultRelationInputProps = DashboardFormComponentProps & {
|
|
556
|
+
entityType?: keyof ReturnType<typeof createEntityConfigs>;
|
|
557
|
+
};
|
|
557
558
|
|
|
558
559
|
export function DefaultRelationInput({
|
|
559
560
|
fieldDef,
|
|
@@ -563,13 +564,14 @@ export function DefaultRelationInput({
|
|
|
563
564
|
name,
|
|
564
565
|
ref,
|
|
565
566
|
disabled,
|
|
566
|
-
|
|
567
|
+
entityType,
|
|
568
|
+
}: Readonly<DefaultRelationInputProps>) {
|
|
567
569
|
const { t } = useLingui();
|
|
568
|
-
if (!fieldDef || !isRelationCustomFieldConfig(fieldDef)) {
|
|
570
|
+
if (!fieldDef || (!isRelationCustomFieldConfig(fieldDef) && !entityType)) {
|
|
569
571
|
return null;
|
|
570
572
|
}
|
|
571
|
-
const entityName = fieldDef.entity;
|
|
572
|
-
const ENTITY_CONFIGS = createEntityConfigs(t);
|
|
573
|
+
const entityName = entityType ?? (fieldDef as RelationCustomFieldConfig).entity;
|
|
574
|
+
const ENTITY_CONFIGS = useMemo(() => createEntityConfigs(t), [t]);
|
|
573
575
|
const config = ENTITY_CONFIGS[entityName as keyof typeof ENTITY_CONFIGS];
|
|
574
576
|
|
|
575
577
|
if (!config) {
|
|
@@ -618,3 +620,7 @@ export function DefaultRelationInput({
|
|
|
618
620
|
);
|
|
619
621
|
}
|
|
620
622
|
}
|
|
623
|
+
|
|
624
|
+
DefaultRelationInput.metadata = {
|
|
625
|
+
isListInput: 'dynamic',
|
|
626
|
+
} as DashboardFormComponentMetadata;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
4
|
+
import { MoneyInput } from './money-input.js';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Form Inputs/MoneyInput',
|
|
8
|
+
component: MoneyInput,
|
|
9
|
+
...withDescription(import.meta.url, './money-input.js'),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'number',
|
|
17
|
+
description: 'The current value in minor units (e.g., cents)',
|
|
18
|
+
},
|
|
19
|
+
currency: {
|
|
20
|
+
control: 'select',
|
|
21
|
+
options: ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD'],
|
|
22
|
+
description: 'The currency code',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
} satisfies Meta<typeof MoneyInput>;
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
type Story = StoryObj<typeof meta>;
|
|
29
|
+
|
|
30
|
+
export const Playground: Story = {
|
|
31
|
+
args: {
|
|
32
|
+
value: 9999,
|
|
33
|
+
currency: 'USD',
|
|
34
|
+
},
|
|
35
|
+
render: args => {
|
|
36
|
+
const { register } = useForm();
|
|
37
|
+
const field = register('playground');
|
|
38
|
+
return (
|
|
39
|
+
<div className="w-[300px]">
|
|
40
|
+
<MoneyInput {...field} {...args} />
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const DifferentCurrencies: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
value: 9999,
|
|
49
|
+
},
|
|
50
|
+
render: (args: any) => {
|
|
51
|
+
const { register } = useForm();
|
|
52
|
+
return (
|
|
53
|
+
<div className="w-[300px] space-y-4">
|
|
54
|
+
<div className="space-y-2">
|
|
55
|
+
<label className="text-sm font-medium">USD</label>
|
|
56
|
+
<MoneyInput {...register('usd')} currency="USD" value={args.value} />
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="space-y-2">
|
|
60
|
+
<label className="text-sm font-medium">EUR</label>
|
|
61
|
+
<MoneyInput {...register('eur')} currency="EUR" value={args.value} />
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div className="space-y-2">
|
|
65
|
+
<label className="text-sm font-medium">GBP</label>
|
|
66
|
+
<MoneyInput {...register('gbp')} currency="GBP" value={args.value} />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="space-y-2">
|
|
70
|
+
<label className="text-sm font-medium">JPY</label>
|
|
71
|
+
<MoneyInput {...register('jpy')} currency="JPY" value={args.value} />
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const LargeAmount: Story = {
|
|
79
|
+
render: () => {
|
|
80
|
+
const { register } = useForm();
|
|
81
|
+
const field = register('large');
|
|
82
|
+
return (
|
|
83
|
+
<div className="w-[300px]">
|
|
84
|
+
<MoneyInput {...field} currency="USD" value={123} />
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { withDescription } from '../../../.storybook/with-description.js';
|
|
4
|
+
import { NumberInput } from './number-input.js';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Form Inputs/NumberInput',
|
|
8
|
+
component: NumberInput,
|
|
9
|
+
...withDescription(import.meta.url, './number-input.js'),
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'number',
|
|
17
|
+
description: 'The current value',
|
|
18
|
+
},
|
|
19
|
+
min: {
|
|
20
|
+
control: 'number',
|
|
21
|
+
description: 'Minimum value',
|
|
22
|
+
},
|
|
23
|
+
max: {
|
|
24
|
+
control: 'number',
|
|
25
|
+
description: 'Maximum value',
|
|
26
|
+
},
|
|
27
|
+
step: {
|
|
28
|
+
control: 'number',
|
|
29
|
+
description: 'Step increment',
|
|
30
|
+
},
|
|
31
|
+
disabled: {
|
|
32
|
+
control: 'boolean',
|
|
33
|
+
description: 'Whether the input is disabled',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
} satisfies Meta<typeof NumberInput>;
|
|
37
|
+
|
|
38
|
+
export default meta;
|
|
39
|
+
type Story = StoryObj<typeof meta>;
|
|
40
|
+
|
|
41
|
+
export const Playground: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
value: 42,
|
|
44
|
+
min: 0,
|
|
45
|
+
max: 100,
|
|
46
|
+
step: 1,
|
|
47
|
+
disabled: false,
|
|
48
|
+
},
|
|
49
|
+
render: args => {
|
|
50
|
+
const { register } = useForm();
|
|
51
|
+
const field = register('playground');
|
|
52
|
+
return (
|
|
53
|
+
<div className="w-[300px]">
|
|
54
|
+
<NumberInput {...field} {...args} />
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Float: Story = {
|
|
61
|
+
render: () => {
|
|
62
|
+
const { register } = useForm();
|
|
63
|
+
const field = register('float');
|
|
64
|
+
return (
|
|
65
|
+
<div className="w-[300px] space-y-2">
|
|
66
|
+
<NumberInput {...field} step={0.01} fieldDef={{ type: 'float' }} />
|
|
67
|
+
<div className="text-sm text-muted-foreground">
|
|
68
|
+
<div>Floating point with step 0.01</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const WithPrefixAndSuffix: Story = {
|
|
76
|
+
render: () => {
|
|
77
|
+
const { register } = useForm();
|
|
78
|
+
const field = register('withAffix');
|
|
79
|
+
return (
|
|
80
|
+
<div className="w-[300px] space-y-2">
|
|
81
|
+
<NumberInput {...field} fieldDef={{ ui: { prefix: '$', suffix: 'USD' } }} step={10} />
|
|
82
|
+
<div className="text-sm text-muted-foreground">
|
|
83
|
+
Demonstrates fieldDef.ui.prefix and fieldDef.ui.suffix
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const NullValue: Story = {
|
|
91
|
+
render: () => {
|
|
92
|
+
const { register } = useForm();
|
|
93
|
+
const field = register('nullValue');
|
|
94
|
+
return (
|
|
95
|
+
<div className="w-[300px] space-y-2">
|
|
96
|
+
<NumberInput {...field} />
|
|
97
|
+
<div className="text-sm text-muted-foreground">
|
|
98
|
+
<div className="mt-1 text-xs">When input is cleared, value becomes null</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
};
|