@vendure/dashboard 3.5.0-minor-202510031341 → 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.
Files changed (220) hide show
  1. package/dist/plugin/dashboard.plugin.js +1 -1
  2. package/dist/plugin/default-page.html +1 -1
  3. package/dist/vite/utils/ast-utils.spec.js +3 -3
  4. package/dist/vite/utils/tsconfig-utils.js +2 -1
  5. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  6. package/dist/vite/vite-plugin-hmr.js +34 -0
  7. package/dist/vite/vite-plugin-theme.js +6 -6
  8. package/dist/vite/vite-plugin-transform-index.js +6 -1
  9. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
  10. package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
  11. package/package.json +18 -5
  12. package/src/app/app-providers.tsx +4 -1
  13. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  14. package/src/app/main.tsx +3 -1
  15. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
  16. package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
  17. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
  18. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
  19. package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
  20. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  21. package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
  22. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
  23. package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
  24. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
  25. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  26. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
  27. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
  28. package/src/app/routes/_authenticated/_global-settings/global-settings.tsx +4 -8
  29. package/src/app/routes/_authenticated/_global-settings/utils/global-languages.ts +268 -0
  30. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
  31. package/src/app/routes/_authenticated/_orders/components/order-address.tsx +15 -15
  32. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +5 -5
  33. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
  34. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
  35. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
  36. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
  37. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
  38. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
  39. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
  40. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
  41. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
  42. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
  43. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
  44. package/src/app/routes/_authenticated/_product-variants/components/add-currency-dropdown.tsx +49 -0
  45. package/src/app/routes/_authenticated/_product-variants/components/add-stock-location-dropdown.tsx +56 -0
  46. package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +12 -0
  47. package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +178 -50
  48. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
  49. package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +0 -11
  50. package/src/app/routes/_authenticated/_products/products.tsx +6 -2
  51. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
  52. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
  53. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
  54. package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
  55. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +3 -10
  56. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
  57. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
  58. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
  59. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
  60. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
  61. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
  62. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
  63. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
  64. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
  65. package/src/app/routes/login.tsx +2 -2
  66. package/src/i18n/locales/ar.po +420 -289
  67. package/src/i18n/locales/cs.po +420 -289
  68. package/src/i18n/locales/de.po +420 -289
  69. package/src/i18n/locales/en.po +420 -289
  70. package/src/i18n/locales/es.po +420 -289
  71. package/src/i18n/locales/fa.po +420 -289
  72. package/src/i18n/locales/fr.po +468 -337
  73. package/src/i18n/locales/he.po +420 -289
  74. package/src/i18n/locales/hr.po +420 -289
  75. package/src/i18n/locales/it.po +420 -289
  76. package/src/i18n/locales/ja.po +420 -289
  77. package/src/i18n/locales/nb.po +420 -289
  78. package/src/i18n/locales/ne.po +420 -289
  79. package/src/i18n/locales/pl.po +420 -289
  80. package/src/i18n/locales/pt_BR.po +420 -289
  81. package/src/i18n/locales/pt_PT.po +420 -289
  82. package/src/i18n/locales/ru.po +420 -289
  83. package/src/i18n/locales/sv.po +420 -289
  84. package/src/i18n/locales/tr.po +420 -289
  85. package/src/i18n/locales/uk.po +420 -289
  86. package/src/i18n/locales/zh_Hans.po +420 -289
  87. package/src/i18n/locales/zh_Hant.po +420 -289
  88. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  89. package/src/lib/components/data-input/affixed-input.tsx +5 -2
  90. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  91. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  92. package/src/lib/components/data-input/customer-group-input.tsx +0 -1
  93. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  94. package/src/lib/components/data-input/datetime-input.tsx +27 -13
  95. package/src/lib/components/data-input/default-relation-input.tsx +18 -12
  96. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  97. package/src/lib/components/data-input/money-input.tsx +7 -11
  98. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  99. package/src/lib/components/data-input/number-input.tsx +16 -5
  100. package/src/lib/components/data-input/password-input.stories.tsx +65 -0
  101. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  102. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  103. package/src/lib/components/data-input/slug-input.tsx +9 -10
  104. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  105. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  106. package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
  107. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  108. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
  109. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  110. package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
  111. package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
  112. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  113. package/src/lib/components/data-table/data-table-filter-badge.tsx +28 -14
  114. package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
  115. package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
  116. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  117. package/src/lib/components/data-table/data-table.tsx +39 -11
  118. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
  119. package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
  120. package/src/lib/components/layout/generated-breadcrumbs.tsx +4 -12
  121. package/src/lib/components/layout/nav-user.tsx +19 -13
  122. package/src/lib/components/login/login-form.tsx +39 -123
  123. package/src/lib/components/shared/alerts.tsx +29 -17
  124. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
  125. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  126. package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
  127. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  128. package/src/lib/components/shared/configurable-operation-input.tsx +1 -1
  129. package/src/lib/components/shared/customer-group-selector.tsx +5 -2
  130. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  131. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  132. package/src/lib/components/shared/facet-value-selector.tsx +130 -34
  133. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  134. package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
  135. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  136. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
  137. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
  138. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
  139. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
  140. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  141. package/src/lib/components/shared/vendure-image.tsx +6 -7
  142. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  143. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  144. package/src/lib/components/ui/alert.stories.tsx +35 -0
  145. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  146. package/src/lib/components/ui/badge.stories.tsx +28 -0
  147. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  148. package/src/lib/components/ui/button.stories.tsx +38 -0
  149. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  150. package/src/lib/components/ui/card.stories.tsx +28 -0
  151. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  152. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  153. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  154. package/src/lib/components/ui/command.stories.tsx +44 -0
  155. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  156. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  157. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  158. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  159. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  160. package/src/lib/components/ui/input-group.tsx +148 -0
  161. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  162. package/src/lib/components/ui/input.stories.tsx +38 -0
  163. package/src/lib/components/ui/label.stories.tsx +24 -0
  164. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  165. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  166. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  167. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  168. package/src/lib/components/ui/password-input.tsx +33 -0
  169. package/src/lib/components/ui/popover.stories.tsx +33 -0
  170. package/src/lib/components/ui/progress.stories.tsx +27 -0
  171. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  172. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  173. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  174. package/src/lib/components/ui/select.stories.tsx +36 -0
  175. package/src/lib/components/ui/separator.stories.tsx +35 -0
  176. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  177. package/src/lib/components/ui/sidebar-context.ts +16 -0
  178. package/src/lib/components/ui/sidebar.tsx +2 -13
  179. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  180. package/src/lib/components/ui/slider.stories.tsx +37 -0
  181. package/src/lib/components/ui/switch.stories.tsx +31 -0
  182. package/src/lib/components/ui/table.stories.tsx +52 -0
  183. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  184. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  185. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  186. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  187. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  188. package/src/lib/components/ui/tooltip.tsx +2 -2
  189. package/src/lib/framework/alert/alert-extensions.tsx +0 -11
  190. package/src/lib/framework/alert/alert-item.tsx +14 -19
  191. package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
  192. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  193. package/src/lib/framework/component-registry/component-registry.tsx +3 -14
  194. package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
  195. package/src/lib/framework/dashboard-widget/latest-orders-widget/index.tsx +0 -2
  196. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
  197. package/src/lib/framework/defaults.ts +9 -13
  198. package/src/lib/framework/extension-api/input-component-extensions.tsx +6 -1
  199. package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
  200. package/src/lib/framework/extension-api/types/alerts.ts +12 -6
  201. package/src/lib/framework/extension-api/types/data-table.ts +5 -2
  202. package/src/lib/framework/extension-api/types/layout.ts +41 -1
  203. package/src/lib/framework/extension-api/types/login.ts +0 -21
  204. package/src/lib/framework/form-engine/value-transformers.ts +8 -1
  205. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  206. package/src/lib/framework/layout-engine/page-layout.tsx +69 -57
  207. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  208. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
  209. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  210. package/src/lib/framework/page/detail-page.tsx +12 -15
  211. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  212. package/src/lib/framework/page/list-page.tsx +8 -1
  213. package/src/lib/graphql/api.ts +18 -1
  214. package/src/lib/graphql/graphql-env.d.ts +1 -1
  215. package/src/lib/hooks/use-alerts.ts +84 -0
  216. package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
  217. package/src/lib/index.ts +12 -5
  218. package/src/lib/providers/alerts-provider.tsx +60 -0
  219. package/src/lib/providers/channel-provider.tsx +1 -0
  220. package/src/lib/providers/theme-provider.tsx +6 -3
@@ -0,0 +1,275 @@
1
+ import { Button } from '@/vdb/components/ui/button.js';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { RouterContextProvider } from '@tanstack/react-router';
4
+ import { createDemoRoute } from '../../../../.storybook/providers.js';
5
+ import {
6
+ FullWidthPageBlock,
7
+ Page,
8
+ PageActionBar,
9
+ PageActionBarLeft,
10
+ PageActionBarRight,
11
+ PageBlock,
12
+ PageLayout,
13
+ PageTitle,
14
+ } from './page-layout.js';
15
+
16
+ const meta = {
17
+ title: 'Layout/Page Layout',
18
+ component: Page,
19
+ parameters: {
20
+ layout: 'fullscreen',
21
+ },
22
+ tags: ['autodocs'],
23
+ } satisfies Meta<typeof Page>;
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ export const Playground: Story = {
29
+ render: () => {
30
+ const { route, router } = createDemoRoute();
31
+ return (
32
+ <RouterContextProvider router={router}>
33
+ <Page pageId="test-page">
34
+ <PageTitle>Test Page</PageTitle>
35
+ <PageLayout>
36
+ <PageBlock column="main" blockId="main-stuff">
37
+ This will display in the main area
38
+ </PageBlock>
39
+ <PageBlock column="side" blockId="side-stuff">
40
+ This will display in the side area
41
+ </PageBlock>
42
+ </PageLayout>
43
+ </Page>
44
+ </RouterContextProvider>
45
+ );
46
+ },
47
+ };
48
+
49
+ export const WithActionBar: Story = {
50
+ render: () => {
51
+ const { route, router } = createDemoRoute();
52
+ return (
53
+ <RouterContextProvider router={router}>
54
+ <Page pageId="product-detail">
55
+ <PageTitle>Product Details</PageTitle>
56
+ <PageActionBar>
57
+ <PageActionBarLeft>
58
+ <Button variant="outline">Cancel</Button>
59
+ </PageActionBarLeft>
60
+ <PageActionBarRight>
61
+ <Button>Save</Button>
62
+ </PageActionBarRight>
63
+ </PageActionBar>
64
+ <PageLayout>
65
+ <PageBlock column="main" blockId="product-info" title="Product Information">
66
+ <div className="space-y-4">
67
+ <div>
68
+ <label className="text-sm font-medium">Name</label>
69
+ <input
70
+ type="text"
71
+ className="w-full border rounded px-3 py-2 mt-1"
72
+ defaultValue="Wireless Headphones"
73
+ />
74
+ </div>
75
+ <div>
76
+ <label className="text-sm font-medium">Description</label>
77
+ <textarea
78
+ className="w-full border rounded px-3 py-2 mt-1"
79
+ rows={4}
80
+ defaultValue="High-quality wireless headphones with active noise cancellation."
81
+ />
82
+ </div>
83
+ </div>
84
+ </PageBlock>
85
+ <PageBlock column="side" blockId="product-meta" title="Metadata">
86
+ <div className="space-y-3">
87
+ <div>
88
+ <div className="text-sm font-medium">Status</div>
89
+ <div className="text-sm text-muted-foreground">Active</div>
90
+ </div>
91
+ <div>
92
+ <div className="text-sm font-medium">SKU</div>
93
+ <div className="text-sm text-muted-foreground">WH-001</div>
94
+ </div>
95
+ <div>
96
+ <div className="text-sm font-medium">Price</div>
97
+ <div className="text-sm text-muted-foreground">$299.00</div>
98
+ </div>
99
+ </div>
100
+ </PageBlock>
101
+ </PageLayout>
102
+ </Page>
103
+ </RouterContextProvider>
104
+ );
105
+ },
106
+ };
107
+
108
+ export const MultipleBlocks: Story = {
109
+ render: () => {
110
+ const { route, router } = createDemoRoute();
111
+ return (
112
+ <RouterContextProvider router={router}>
113
+ <Page pageId="complex-page">
114
+ <PageTitle>Complex Page Layout</PageTitle>
115
+ <PageLayout>
116
+ <PageBlock
117
+ column="main"
118
+ blockId="block-1"
119
+ title="Main Block 1"
120
+ description="This is the first main block"
121
+ >
122
+ <p>Content for the first main block goes here.</p>
123
+ </PageBlock>
124
+ <PageBlock
125
+ column="main"
126
+ blockId="block-2"
127
+ title="Main Block 2"
128
+ description="This is the second main block"
129
+ >
130
+ <p>Content for the second main block goes here.</p>
131
+ </PageBlock>
132
+ <PageBlock column="side" blockId="side-1" title="Sidebar Block 1">
133
+ <p>First sidebar block content.</p>
134
+ </PageBlock>
135
+ <PageBlock column="side" blockId="side-2" title="Sidebar Block 2">
136
+ <p>Second sidebar block content.</p>
137
+ </PageBlock>
138
+ <PageBlock column="side" blockId="side-3" title="Sidebar Block 3">
139
+ <p>Third sidebar block content.</p>
140
+ </PageBlock>
141
+ </PageLayout>
142
+ </Page>
143
+ </RouterContextProvider>
144
+ );
145
+ },
146
+ };
147
+
148
+ export const WithFullWidthBlock: Story = {
149
+ render: () => {
150
+ const { route, router } = createDemoRoute();
151
+ return (
152
+ <RouterContextProvider router={router}>
153
+ <Page pageId="dashboard-overview">
154
+ <PageTitle>Dashboard Overview</PageTitle>
155
+ <PageLayout>
156
+ <FullWidthPageBlock blockId="stats">
157
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-4 p-6 bg-muted/50 rounded-lg">
158
+ <div className="text-center">
159
+ <div className="text-3xl font-bold">1,234</div>
160
+ <div className="text-sm text-muted-foreground">Total Orders</div>
161
+ </div>
162
+ <div className="text-center">
163
+ <div className="text-3xl font-bold">$45,678</div>
164
+ <div className="text-sm text-muted-foreground">Revenue</div>
165
+ </div>
166
+ <div className="text-center">
167
+ <div className="text-3xl font-bold">567</div>
168
+ <div className="text-sm text-muted-foreground">Products</div>
169
+ </div>
170
+ <div className="text-center">
171
+ <div className="text-3xl font-bold">890</div>
172
+ <div className="text-sm text-muted-foreground">Customers</div>
173
+ </div>
174
+ </div>
175
+ </FullWidthPageBlock>
176
+ <PageBlock column="main" blockId="recent-orders" title="Recent Orders">
177
+ <div className="space-y-2">
178
+ {[1, 2, 3].map(i => (
179
+ <div key={i} className="flex justify-between py-2 border-b">
180
+ <span>Order #{1000 + i}</span>
181
+ <span className="text-muted-foreground">$99.00</span>
182
+ </div>
183
+ ))}
184
+ </div>
185
+ </PageBlock>
186
+ <PageBlock column="side" blockId="quick-stats" title="Quick Stats">
187
+ <div className="space-y-3">
188
+ <div>
189
+ <div className="text-sm font-medium">Pending Orders</div>
190
+ <div className="text-2xl font-bold">12</div>
191
+ </div>
192
+ <div>
193
+ <div className="text-sm font-medium">Low Stock Items</div>
194
+ <div className="text-2xl font-bold">5</div>
195
+ </div>
196
+ </div>
197
+ </PageBlock>
198
+ </PageLayout>
199
+ </Page>
200
+ </RouterContextProvider>
201
+ );
202
+ },
203
+ };
204
+
205
+ export const MinimalPage: Story = {
206
+ render: () => {
207
+ const { route, router } = createDemoRoute();
208
+ return (
209
+ <RouterContextProvider router={router}>
210
+ <Page pageId="simple-page">
211
+ <PageTitle>Simple Page</PageTitle>
212
+ <PageLayout>
213
+ <PageBlock column="main" blockId="content">
214
+ <p>This is a minimal page with just a title and one content block.</p>
215
+ </PageBlock>
216
+ </PageLayout>
217
+ </Page>
218
+ </RouterContextProvider>
219
+ );
220
+ },
221
+ };
222
+
223
+ export const WithBlockDescriptions: Story = {
224
+ render: () => {
225
+ const { route, router } = createDemoRoute();
226
+ return (
227
+ <RouterContextProvider router={router}>
228
+ <Page pageId="settings-page">
229
+ <PageTitle>Settings</PageTitle>
230
+ <PageLayout>
231
+ <PageBlock
232
+ column="main"
233
+ blockId="general"
234
+ title="General Settings"
235
+ description="Configure general application settings and preferences"
236
+ >
237
+ <div className="space-y-4">
238
+ <div className="flex items-center justify-between">
239
+ <label className="text-sm font-medium">Enable notifications</label>
240
+ <input type="checkbox" defaultChecked />
241
+ </div>
242
+ <div className="flex items-center justify-between">
243
+ <label className="text-sm font-medium">Dark mode</label>
244
+ <input type="checkbox" />
245
+ </div>
246
+ </div>
247
+ </PageBlock>
248
+ <PageBlock
249
+ column="main"
250
+ blockId="advanced"
251
+ title="Advanced Settings"
252
+ description="Advanced configuration options for power users"
253
+ >
254
+ <div className="space-y-4">
255
+ <div>
256
+ <label className="text-sm font-medium">API Key</label>
257
+ <input
258
+ type="text"
259
+ className="w-full border rounded px-3 py-2 mt-1"
260
+ defaultValue="sk_test_..."
261
+ />
262
+ </div>
263
+ </div>
264
+ </PageBlock>
265
+ <PageBlock column="side" blockId="help" title="Help" description="Need assistance?">
266
+ <Button variant="outline" className="w-full">
267
+ View Documentation
268
+ </Button>
269
+ </PageBlock>
270
+ </PageLayout>
271
+ </Page>
272
+ </RouterContextProvider>
273
+ );
274
+ },
275
+ };
@@ -7,17 +7,46 @@ export type NavMenuSectionPlacement = 'top' | 'bottom';
7
7
 
8
8
  /**
9
9
  * @description
10
- * The base configuration for navigation items and sections of the main app nav bar.
10
+ * Defines an items in the navigation menu.
11
11
  *
12
12
  * @docsCategory extensions-api
13
13
  * @docsPage Navigation
14
14
  * @since 3.4.0
15
15
  */
16
- interface NavMenuBaseItem {
16
+ interface NavMenuItem {
17
+ /**
18
+ * @description
19
+ * A unique ID for this nav menu item
20
+ */
17
21
  id: string;
22
+ /**
23
+ * @description
24
+ * The title that will appear in the nav menu
25
+ */
18
26
  title: string;
27
+ /**
28
+ * @description
29
+ * The url of the route which this nav item links to.
30
+ */
31
+ url: string;
32
+ /**
33
+ * @description
34
+ * An optional icon component to represent the item,
35
+ * which should be imported from `lucide-react`.
36
+ */
19
37
  icon?: LucideIcon;
38
+ /**
39
+ * @description
40
+ * The order is an number which allows you to control
41
+ * the relative position in relation to other items in the
42
+ * menu.
43
+ * A higher number appears further down the list.
44
+ */
20
45
  order?: number;
46
+ /**
47
+ * Whether this item should appear in the top of bottom section
48
+ * of the nav menu.
49
+ */
21
50
  placement?: NavMenuSectionPlacement;
22
51
  /**
23
52
  * @description
@@ -27,23 +56,7 @@ interface NavMenuBaseItem {
27
56
  requiresPermission?: string | string[];
28
57
  }
29
58
 
30
- /**
31
- * @description
32
- * Defines an items in the navigation menu.
33
- *
34
- * @docsCategory extensions-api
35
- * @docsPage Navigation
36
- * @since 3.4.0
37
- */
38
- export interface NavMenuItem extends NavMenuBaseItem {
39
- /**
40
- * @description
41
- * The url of the route which this nav item links to.
42
- */
43
- url: string;
44
- }
45
-
46
- export interface NavMenuSection extends NavMenuBaseItem {
59
+ export interface NavMenuSection extends Omit<NavMenuItem, 'url'> {
47
60
  defaultOpen?: boolean;
48
61
  items?: NavMenuItem[];
49
62
  }
@@ -0,0 +1,151 @@
1
+ import { graphql } from '@/vdb/graphql/graphql.js';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { DemoRouterProvider } from '../../../../.storybook/providers.js';
4
+ import { DetailPage, DetailPageProps } from './detail-page.js';
5
+
6
+ // Sample GraphQL query for a product detail
7
+ const productFragment = graphql(`
8
+ fragment ProductDetail on Product {
9
+ id
10
+ createdAt
11
+ updatedAt
12
+ name
13
+ slug
14
+ description
15
+ enabled
16
+ featuredAsset {
17
+ id
18
+ }
19
+ assets {
20
+ id
21
+ }
22
+ facetValues {
23
+ id
24
+ }
25
+ translations {
26
+ id
27
+ languageCode
28
+ name
29
+ slug
30
+ description
31
+ }
32
+ }
33
+ `);
34
+
35
+ const productQuery = graphql(
36
+ `
37
+ query Product($id: ID!) {
38
+ product(id: $id) {
39
+ ...ProductDetail
40
+ }
41
+ }
42
+ `,
43
+ [productFragment],
44
+ );
45
+
46
+ const createProductDocument = graphql(`
47
+ mutation CreateProduct($input: CreateProductInput!) {
48
+ createProduct(input: $input) {
49
+ id
50
+ name
51
+ slug
52
+ description
53
+ enabled
54
+ }
55
+ }
56
+ `);
57
+
58
+ const updateProductDocument = graphql(`
59
+ mutation UpdateProduct($input: UpdateProductInput!) {
60
+ updateProduct(input: $input) {
61
+ id
62
+ name
63
+ slug
64
+ description
65
+ enabled
66
+ }
67
+ }
68
+ `);
69
+
70
+ function DetailPageStoryWrapper(props: Omit<DetailPageProps<any, any, any>, 'route'>) {
71
+ return (
72
+ <DemoRouterProvider
73
+ component={route => <DetailPage {...props} route={route} />}
74
+ path={'/products/$id'}
75
+ initialPath={'/products/1'}
76
+ />
77
+ );
78
+ }
79
+
80
+ const meta = {
81
+ title: 'Layout/DetailPage',
82
+ component: DetailPageStoryWrapper,
83
+ parameters: {
84
+ layout: 'fullscreen',
85
+ },
86
+ tags: ['autodocs'],
87
+ argTypes: {
88
+ pageId: {
89
+ control: 'text',
90
+ description: 'Unique identifier for the detail page',
91
+ },
92
+ entityName: {
93
+ control: 'text',
94
+ description: 'Name of the entity (inferred from query if not provided)',
95
+ },
96
+ title: {
97
+ control: false,
98
+ description: 'Function that returns the page title based on the entity',
99
+ },
100
+ queryDocument: {
101
+ control: false,
102
+ description: 'GraphQL query document for fetching entity data',
103
+ },
104
+ createDocument: {
105
+ control: false,
106
+ description: 'GraphQL mutation document for creating the entity',
107
+ },
108
+ updateDocument: {
109
+ control: false,
110
+ description: 'GraphQL mutation document for updating the entity',
111
+ },
112
+ setValuesForUpdate: {
113
+ control: false,
114
+ description: 'Function to map entity data to update input',
115
+ },
116
+ },
117
+ } satisfies Meta<typeof DetailPageStoryWrapper>;
118
+
119
+ export default meta;
120
+ type Story = StoryObj<typeof meta>;
121
+
122
+ /**
123
+ * Basic example of a DetailPage showing a product entity.
124
+ * This demonstrates the minimal configuration needed to render a detail page.
125
+ */
126
+ export const BasicDetail: Story = {
127
+ args: {
128
+ pageId: 'product-detail',
129
+ queryDocument: productQuery,
130
+ updateDocument: updateProductDocument,
131
+ title: entity => entity?.name || 'Product',
132
+ setValuesForUpdate: entity => ({
133
+ id: entity.id,
134
+ name: entity.name,
135
+ slug: entity.slug,
136
+ description: entity.description,
137
+ enabled: entity.enabled,
138
+ featuredAssetId: entity.featuredAsset?.id,
139
+ assetIds: entity.assets.map(asset => asset.id),
140
+ facetValueIds: entity.facetValues.map(facetValue => facetValue.id),
141
+ translations: entity.translations.map(translation => ({
142
+ id: translation.id,
143
+ languageCode: translation.languageCode,
144
+ name: translation.name,
145
+ slug: translation.slug,
146
+ description: translation.description,
147
+ customFields: (translation as any).customFields,
148
+ })),
149
+ }),
150
+ },
151
+ };
@@ -5,8 +5,8 @@ import { Checkbox } from '@/vdb/components/ui/checkbox.js';
5
5
  import { Input } from '@/vdb/components/ui/input.js';
6
6
  import { NEW_ENTITY_PATH } from '@/vdb/constants.js';
7
7
  import { useDetailPage } from '@/vdb/framework/page/use-detail-page.js';
8
- import { Trans } from '@lingui/react/macro';
9
8
  import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
9
+ import { Trans } from '@lingui/react/macro';
10
10
  import { AnyRoute, useNavigate } from '@tanstack/react-router';
11
11
  import { ResultOf, VariablesOf } from 'gql.tada';
12
12
  import { toast } from 'sonner';
@@ -16,6 +16,7 @@ import {
16
16
  getOperationVariablesFields,
17
17
  } from '../document-introspection/get-document-structure.js';
18
18
 
19
+ import { NumberInput } from '@/vdb/components/data-input/number-input.js';
19
20
  import { TranslatableFormFieldWrapper } from '@/vdb/components/shared/translatable-form-field.js';
20
21
  import { FormControl } from '@/vdb/components/ui/form.js';
21
22
  import { ControllerRenderProps, FieldPath, FieldValues } from 'react-hook-form';
@@ -108,11 +109,7 @@ function FieldInputRenderer<
108
109
  case 'Float':
109
110
  return (
110
111
  <FormControl>
111
- <Input
112
- type="number"
113
- value={field.value}
114
- onChange={e => field.onChange(e.target.valueAsNumber)}
115
- />
112
+ <NumberInput {...field} />
116
113
  </FormControl>
117
114
  );
118
115
  case 'DateTime':
@@ -152,15 +149,15 @@ export function DetailPage<
152
149
  C extends TypedDocumentNode<any, any>,
153
150
  U extends TypedDocumentNode<any, any>,
154
151
  >({
155
- pageId,
156
- route,
157
- entityName: passedEntityName,
158
- queryDocument,
159
- createDocument,
160
- updateDocument,
161
- setValuesForUpdate,
162
- title,
163
- }: DetailPageProps<T, C, U>) {
152
+ pageId,
153
+ route,
154
+ entityName: passedEntityName,
155
+ queryDocument,
156
+ createDocument,
157
+ updateDocument,
158
+ setValuesForUpdate,
159
+ title,
160
+ }: DetailPageProps<T, C, U>) {
164
161
  const params = route.useParams();
165
162
  const creatingNewEntity = params.id === NEW_ENTITY_PATH;
166
163
  const navigate = useNavigate();