@vendure/dashboard 3.5.0-minor-202510071456 → 3.5.0-minor-202510201346

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 (204) hide show
  1. package/dist/plugin/dashboard.plugin.js +1 -1
  2. package/dist/vite/utils/ast-utils.spec.js +3 -3
  3. package/dist/vite/vite-plugin-hmr.d.ts +8 -0
  4. package/dist/vite/vite-plugin-hmr.js +34 -0
  5. package/dist/vite/vite-plugin-theme.js +6 -6
  6. package/dist/vite/vite-plugin-transform-index.js +6 -1
  7. package/dist/vite/vite-plugin-vendure-dashboard.d.ts +31 -4
  8. package/dist/vite/vite-plugin-vendure-dashboard.js +89 -34
  9. package/package.json +17 -5
  10. package/src/app/app-providers.tsx +4 -1
  11. package/src/app/common/map-faceted-filter-fields.ts +21 -0
  12. package/src/app/main.tsx +3 -1
  13. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +2 -2
  14. package/src/app/routes/_authenticated/_administrators/administrators.tsx +13 -3
  15. package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +6 -13
  16. package/src/app/routes/_authenticated/_administrators/components/role-permissions-display.tsx +1 -1
  17. package/src/app/routes/_authenticated/_assets/assets.tsx +17 -1
  18. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +1 -0
  19. package/src/app/routes/_authenticated/_collections/collections.tsx +5 -0
  20. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +0 -1
  21. package/src/app/routes/_authenticated/_customers/customers.tsx +9 -5
  22. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +0 -6
  23. package/src/app/routes/_authenticated/_facets/components/facet-value-bulk-actions.tsx +16 -0
  24. package/src/app/routes/_authenticated/_facets/components/facet-values-table.tsx +43 -12
  25. package/src/app/routes/_authenticated/_facets/facets_.$facetId.values_.$id.tsx +14 -5
  26. package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +117 -92
  27. package/src/app/routes/_authenticated/_orders/components/order-detail-shared.tsx +1 -1
  28. package/src/app/routes/_authenticated/_orders/components/order-modification-summary.tsx +2 -1
  29. package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +26 -27
  30. package/src/app/routes/_authenticated/_orders/components/order-table.tsx +5 -3
  31. package/src/app/routes/_authenticated/_orders/components/state-transition-control.tsx +6 -9
  32. package/src/app/routes/_authenticated/_orders/orders.graphql.ts +17 -1
  33. package/src/app/routes/_authenticated/_orders/orders.tsx +2 -0
  34. package/src/app/routes/_authenticated/_orders/orders_.$id_.modify.tsx +48 -281
  35. package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +59 -40
  36. package/src/app/routes/_authenticated/_orders/utils/order-utils.ts +73 -0
  37. package/src/app/routes/_authenticated/_orders/utils/use-modify-order.ts +312 -0
  38. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +2 -2
  39. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +4 -0
  40. package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +2 -0
  41. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +0 -6
  42. package/src/app/routes/_authenticated/_products/products.tsx +6 -2
  43. package/src/app/routes/_authenticated/_products/products_.$productId.option-groups.$productOptionGroupId.options_.$id.tsx +4 -8
  44. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +0 -10
  45. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +2 -2
  46. package/src/app/routes/_authenticated/_promotions/promotions.tsx +12 -0
  47. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -2
  48. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +2 -2
  49. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +2 -2
  50. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +4 -0
  51. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +4 -10
  52. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +2 -2
  53. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +2 -2
  54. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +9 -0
  55. package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +1 -0
  56. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +2 -2
  57. package/src/app/routes/login.tsx +2 -2
  58. package/src/i18n/locales/ar.po +420 -289
  59. package/src/i18n/locales/cs.po +420 -289
  60. package/src/i18n/locales/de.po +420 -289
  61. package/src/i18n/locales/en.po +420 -289
  62. package/src/i18n/locales/es.po +420 -289
  63. package/src/i18n/locales/fa.po +420 -289
  64. package/src/i18n/locales/fr.po +468 -337
  65. package/src/i18n/locales/he.po +420 -289
  66. package/src/i18n/locales/hr.po +420 -289
  67. package/src/i18n/locales/it.po +420 -289
  68. package/src/i18n/locales/ja.po +420 -289
  69. package/src/i18n/locales/nb.po +420 -289
  70. package/src/i18n/locales/ne.po +420 -289
  71. package/src/i18n/locales/pl.po +420 -289
  72. package/src/i18n/locales/pt_BR.po +420 -289
  73. package/src/i18n/locales/pt_PT.po +420 -289
  74. package/src/i18n/locales/ru.po +420 -289
  75. package/src/i18n/locales/sv.po +420 -289
  76. package/src/i18n/locales/tr.po +420 -289
  77. package/src/i18n/locales/uk.po +420 -289
  78. package/src/i18n/locales/zh_Hans.po +420 -289
  79. package/src/i18n/locales/zh_Hant.po +420 -289
  80. package/src/lib/components/data-input/affixed-input.stories.tsx +93 -0
  81. package/src/lib/components/data-input/affixed-input.tsx +5 -2
  82. package/src/lib/components/data-input/boolean-input.stories.tsx +102 -0
  83. package/src/lib/components/data-input/checkbox-input.stories.tsx +61 -0
  84. package/src/lib/components/data-input/datetime-input.stories.tsx +62 -0
  85. package/src/lib/components/data-input/datetime-input.tsx +27 -13
  86. package/src/lib/components/data-input/default-relation-input.tsx +18 -12
  87. package/src/lib/components/data-input/money-input.stories.tsx +88 -0
  88. package/src/lib/components/data-input/number-input.stories.tsx +103 -0
  89. package/src/lib/components/data-input/number-input.tsx +10 -4
  90. package/src/lib/components/data-input/password-form-input.stories.tsx +65 -0
  91. package/src/lib/components/data-input/{password-input.tsx → password-form-input.tsx} +1 -1
  92. package/src/lib/components/data-input/rich-text-input.stories.tsx +92 -0
  93. package/src/lib/components/data-input/slug-input.stories.tsx +232 -0
  94. package/src/lib/components/data-input/slug-input.tsx +9 -10
  95. package/src/lib/components/data-input/text-input.stories.tsx +52 -0
  96. package/src/lib/components/data-input/textarea-input.stories.tsx +55 -0
  97. package/src/lib/components/data-table/add-filter-menu.tsx +6 -1
  98. package/src/lib/components/data-table/column-header-wrapper.tsx +106 -0
  99. package/src/lib/components/data-table/data-table-bulk-action-item.tsx +11 -9
  100. package/src/lib/components/data-table/data-table-bulk-actions.tsx +4 -4
  101. package/src/lib/components/data-table/data-table-column-header.tsx +17 -14
  102. package/src/lib/components/data-table/data-table-faceted-filter.tsx +33 -11
  103. package/src/lib/components/data-table/data-table-filter-badge-editable.tsx +35 -0
  104. package/src/lib/components/data-table/data-table-filter-badge.tsx +23 -16
  105. package/src/lib/components/data-table/data-table-filter-dialog.tsx +28 -8
  106. package/src/lib/components/data-table/data-table-pagination.tsx +23 -7
  107. package/src/lib/components/data-table/data-table.stories.tsx +249 -0
  108. package/src/lib/components/data-table/data-table.tsx +37 -9
  109. package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +79 -34
  110. package/src/lib/components/data-table/use-generated-columns.tsx +55 -27
  111. package/src/lib/components/layout/nav-user.tsx +19 -13
  112. package/src/lib/components/login/login-form.tsx +39 -123
  113. package/src/lib/components/shared/alerts.tsx +29 -17
  114. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +3 -3
  115. package/src/lib/components/shared/asset/asset-gallery.stories.tsx +76 -0
  116. package/src/lib/components/shared/asset/asset-gallery.tsx +147 -113
  117. package/src/lib/components/shared/asset/asset-picker-dialog.stories.tsx +58 -0
  118. package/src/lib/components/shared/customer-group-selector.tsx +5 -2
  119. package/src/lib/components/shared/detail-page-button.stories.tsx +52 -0
  120. package/src/lib/components/shared/facet-value-selector.stories.tsx +48 -0
  121. package/src/lib/components/shared/facet-value-selector.tsx +130 -34
  122. package/src/lib/components/shared/paginated-list-data-table.stories.tsx +212 -0
  123. package/src/lib/components/shared/paginated-list-data-table.tsx +12 -12
  124. package/src/lib/components/shared/permission-guard.stories.tsx +46 -0
  125. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +2 -0
  126. package/src/lib/components/shared/rich-text-editor/responsive-toolbar.tsx +8 -4
  127. package/src/lib/components/shared/rich-text-editor/rich-text-editor.tsx +1 -0
  128. package/src/lib/components/shared/table-cell/order-table-cell-components.tsx +40 -0
  129. package/src/lib/components/shared/vendure-image.stories.tsx +167 -0
  130. package/src/lib/components/shared/vendure-image.tsx +6 -7
  131. package/src/lib/components/ui/accordion.stories.tsx +33 -0
  132. package/src/lib/components/ui/alert-dialog.stories.tsx +48 -0
  133. package/src/lib/components/ui/alert.stories.tsx +35 -0
  134. package/src/lib/components/ui/aspect-ratio.stories.tsx +28 -0
  135. package/src/lib/components/ui/badge.stories.tsx +28 -0
  136. package/src/lib/components/ui/breadcrumb.stories.tsx +41 -0
  137. package/src/lib/components/ui/button.stories.tsx +38 -0
  138. package/src/lib/components/ui/calendar.stories.tsx +22 -0
  139. package/src/lib/components/ui/card.stories.tsx +28 -0
  140. package/src/lib/components/ui/carousel.stories.tsx +34 -0
  141. package/src/lib/components/ui/checkbox.stories.tsx +31 -0
  142. package/src/lib/components/ui/collapsible.stories.tsx +39 -0
  143. package/src/lib/components/ui/command.stories.tsx +44 -0
  144. package/src/lib/components/ui/context-menu.stories.tsx +38 -0
  145. package/src/lib/components/ui/dialog.stories.tsx +52 -0
  146. package/src/lib/components/ui/drawer.stories.tsx +50 -0
  147. package/src/lib/components/ui/dropdown-menu.stories.tsx +41 -0
  148. package/src/lib/components/ui/hover-card.stories.tsx +38 -0
  149. package/src/lib/components/ui/input-group.tsx +148 -0
  150. package/src/lib/components/ui/input-otp.stories.tsx +30 -0
  151. package/src/lib/components/ui/input.stories.tsx +38 -0
  152. package/src/lib/components/ui/label.stories.tsx +24 -0
  153. package/src/lib/components/ui/menubar.stories.tsx +53 -0
  154. package/src/lib/components/ui/navigation-menu.stories.tsx +54 -0
  155. package/src/lib/components/ui/pagination.stories.tsx +51 -0
  156. package/src/lib/components/ui/password-input.stories.tsx +32 -0
  157. package/src/lib/components/ui/password-input.tsx +29 -0
  158. package/src/lib/components/ui/popover.stories.tsx +33 -0
  159. package/src/lib/components/ui/progress.stories.tsx +27 -0
  160. package/src/lib/components/ui/radio-group.stories.tsx +34 -0
  161. package/src/lib/components/ui/resizable.stories.tsx +32 -0
  162. package/src/lib/components/ui/scroll-area.stories.tsx +31 -0
  163. package/src/lib/components/ui/select.stories.tsx +36 -0
  164. package/src/lib/components/ui/separator.stories.tsx +35 -0
  165. package/src/lib/components/ui/sheet.stories.tsx +50 -0
  166. package/src/lib/components/ui/sidebar-context.ts +16 -0
  167. package/src/lib/components/ui/sidebar.tsx +2 -13
  168. package/src/lib/components/ui/skeleton.stories.tsx +26 -0
  169. package/src/lib/components/ui/slider.stories.tsx +37 -0
  170. package/src/lib/components/ui/switch.stories.tsx +31 -0
  171. package/src/lib/components/ui/table.stories.tsx +52 -0
  172. package/src/lib/components/ui/tabs.stories.tsx +29 -0
  173. package/src/lib/components/ui/textarea.stories.tsx +32 -0
  174. package/src/lib/components/ui/toggle-group.stories.tsx +31 -0
  175. package/src/lib/components/ui/toggle.stories.tsx +39 -0
  176. package/src/lib/components/ui/tooltip.stories.tsx +30 -0
  177. package/src/lib/components/ui/tooltip.tsx +2 -2
  178. package/src/lib/framework/alert/alert-extensions.tsx +0 -11
  179. package/src/lib/framework/alert/alert-item.tsx +14 -19
  180. package/src/lib/framework/alert/alerts-indicator.tsx +14 -15
  181. package/src/lib/framework/alert/search-index-buffer-alert/search-index-buffer-alert.ts +41 -0
  182. package/src/lib/framework/component-registry/component-registry.tsx +3 -14
  183. package/src/lib/framework/dashboard-widget/base-widget.tsx +18 -9
  184. package/src/lib/framework/dashboard-widget/widget-filters-context.tsx +12 -11
  185. package/src/lib/framework/defaults.ts +9 -13
  186. package/src/lib/framework/extension-api/input-component-extensions.tsx +8 -3
  187. package/src/lib/framework/extension-api/logic/alerts.ts +3 -2
  188. package/src/lib/framework/extension-api/types/alerts.ts +12 -6
  189. package/src/lib/framework/extension-api/types/data-table.ts +5 -2
  190. package/src/lib/framework/extension-api/types/login.ts +0 -21
  191. package/src/lib/framework/layout-engine/custom-form-page.stories.tsx +344 -0
  192. package/src/lib/framework/layout-engine/page-layout.tsx +11 -9
  193. package/src/lib/framework/layout-engine/page.stories.tsx +275 -0
  194. package/src/lib/framework/nav-menu/nav-menu-extensions.ts +32 -19
  195. package/src/lib/framework/page/detail-page.stories.tsx +151 -0
  196. package/src/lib/framework/page/list-page.stories.tsx +217 -0
  197. package/src/lib/framework/page/list-page.tsx +8 -1
  198. package/src/lib/graphql/api.ts +18 -1
  199. package/src/lib/graphql/graphql-env.d.ts +1 -1
  200. package/src/lib/hooks/use-alerts.ts +84 -0
  201. package/src/lib/hooks/use-floating-bulk-actions.ts +2 -3
  202. package/src/lib/index.ts +14 -1
  203. package/src/lib/providers/alerts-provider.tsx +60 -0
  204. package/src/lib/providers/theme-provider.tsx +6 -3
@@ -27,7 +27,7 @@ import { Dialog, DialogTrigger } from '../ui/dialog.js';
27
27
  import { LanguageDialog } from './language-dialog.js';
28
28
 
29
29
  export function NavUser() {
30
- const { isMobile } = useSidebar();
30
+ const { isMobile, state } = useSidebar();
31
31
  const router = useRouter();
32
32
  const navigate = useNavigate();
33
33
  const { humanReadableLanguage } = useDisplayLocale();
@@ -65,13 +65,15 @@ export function NavUser() {
65
65
  <div className="relative flex rounded-lg border justify-center items-center w-8 h-8">
66
66
  {avatarFallback}
67
67
  </div>
68
- <div className="grid flex-1 text-left text-sm leading-tight">
69
- <span className="truncate font-semibold">
70
- {user.firstName} {user.lastName}
71
- </span>
72
- <span className="truncate text-xs">{user.emailAddress}</span>
73
- </div>
74
- <ChevronsUpDown className="ml-auto size-4" />
68
+ {state === 'expanded' ? (
69
+ <div className="grid flex-1 text-left text-sm leading-tight">
70
+ <span className="truncate font-semibold">
71
+ {user.firstName} {user.lastName}
72
+ </span>
73
+ <span className="truncate text-xs">{user.emailAddress}</span>
74
+ </div>
75
+ ) : null}
76
+ {state === 'expanded' ? <ChevronsUpDown className="ml-auto size-4" /> : null}
75
77
  </SidebarMenuButton>
76
78
  </DropdownMenuTrigger>
77
79
  <DropdownMenuContent
@@ -115,7 +117,9 @@ export function NavUser() {
115
117
  </DropdownMenuItem>
116
118
  </DialogTrigger>
117
119
  <DropdownMenuSub>
118
- <DropdownMenuSubTrigger><Trans>Theme</Trans></DropdownMenuSubTrigger>
120
+ <DropdownMenuSubTrigger>
121
+ <Trans>Theme</Trans>
122
+ </DropdownMenuSubTrigger>
119
123
  <DropdownMenuPortal>
120
124
  <DropdownMenuSubContent>
121
125
  <DropdownMenuRadioGroup
@@ -124,15 +128,15 @@ export function NavUser() {
124
128
  >
125
129
  <DropdownMenuRadioItem value="light">
126
130
  <Sun />
127
- <Trans context='theme'>Light</Trans>
131
+ <Trans context="theme">Light</Trans>
128
132
  </DropdownMenuRadioItem>
129
133
  <DropdownMenuRadioItem value="dark">
130
134
  <Moon />
131
- <Trans context='theme'>Dark</Trans>
135
+ <Trans context="theme">Dark</Trans>
132
136
  </DropdownMenuRadioItem>
133
137
  <DropdownMenuRadioItem value="system">
134
138
  <Monitor />
135
- <Trans context='theme'>System</Trans>
139
+ <Trans context="theme">System</Trans>
136
140
  </DropdownMenuRadioItem>
137
141
  </DropdownMenuRadioGroup>
138
142
  </DropdownMenuSubContent>
@@ -141,7 +145,9 @@ export function NavUser() {
141
145
  </DropdownMenuGroup>
142
146
  {isDevMode && (
143
147
  <DropdownMenuSub>
144
- <DropdownMenuSubTrigger><Trans>Dev Mode</Trans></DropdownMenuSubTrigger>
148
+ <DropdownMenuSubTrigger>
149
+ <Trans>Dev Mode</Trans>
150
+ </DropdownMenuSubTrigger>
145
151
  <DropdownMenuPortal>
146
152
  <DropdownMenuSubContent>
147
153
  <DropdownMenuRadioGroup
@@ -1,31 +1,27 @@
1
1
  import { Button } from '@/vdb/components/ui/button.js';
2
2
  import { Card, CardContent } from '@/vdb/components/ui/card.js';
3
3
  import { Input } from '@/vdb/components/ui/input.js';
4
- import { Trans } from '@lingui/react/macro';
4
+ import { PasswordInput } from '@/vdb/components/ui/password-input.js';
5
5
  import { cn } from '@/vdb/lib/utils.js';
6
6
  import { zodResolver } from '@hookform/resolvers/zod';
7
+ import { Trans, useLingui } from '@lingui/react/macro';
7
8
  import { Loader2 } from 'lucide-react';
8
9
  import * as React from 'react';
9
10
  import { useForm } from 'react-hook-form';
10
11
  import { toast } from 'sonner';
11
- import { uiConfig } from 'virtual:vendure-ui-config';
12
12
  import { z } from 'zod';
13
13
  import { useLoginExtensions } from '../../framework/extension-api/use-login-extensions.js';
14
14
  import { LogoMark } from '../shared/logo-mark.js';
15
- import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form.js';
15
+ import { Form, FormControl, FormField, FormItem, FormMessage } from '../ui/form.js';
16
16
  import { Separator } from '../ui/separator.js';
17
17
 
18
- export interface LoginFormProps extends React.ComponentProps<'div'> {
19
- loginError?: string;
20
- isVerifying?: boolean;
21
- onFormSubmit?: (username: string, password: string) => void;
22
- }
23
-
24
- export type RemoteLoginImage = {
25
- urls: { regular: string };
26
- location: { name: string };
27
- user: { name: string; links: { html: string } };
28
- };
18
+ export type LoginFormProps = Readonly<
19
+ {
20
+ loginError?: string;
21
+ isVerifying?: boolean;
22
+ onFormSubmit?: (username: string, password: string) => void;
23
+ } & React.ComponentProps<'div'>
24
+ >;
29
25
 
30
26
  const formSchema = z.object({
31
27
  username: z.string().min(1),
@@ -33,16 +29,8 @@ const formSchema = z.object({
33
29
  });
34
30
 
35
31
  export function LoginForm({ className, onFormSubmit, isVerifying, loginError, ...props }: LoginFormProps) {
36
- const [remoteLoginImage, setRemoteLoginImage] = React.useState<RemoteLoginImage | null>(null);
37
32
  const loginExtensions = useLoginExtensions();
38
-
39
- React.useEffect(() => {
40
- if (!uiConfig.loginImageUrl) {
41
- fetch('https://login-image.vendure.io')
42
- .then(res => res.json())
43
- .then(data => setRemoteLoginImage(data));
44
- }
45
- }, []);
33
+ const { t } = useLingui();
46
34
 
47
35
  React.useEffect(() => {
48
36
  if (loginError && !isVerifying) {
@@ -59,60 +47,41 @@ export function LoginForm({ className, onFormSubmit, isVerifying, loginError, ..
59
47
  });
60
48
 
61
49
  return (
62
- <div className={cn('flex flex-col gap-6 bg-sidebar', className)} {...props}>
63
- <Card className="overflow-hidden">
64
- <CardContent className="grid p-0 md:grid-cols-2">
50
+ <div className={cn('flex flex-col items-center gap-6', className)} {...props}>
51
+ {loginExtensions.logo ? (
52
+ <loginExtensions.logo.component />
53
+ ) : (
54
+ <LogoMark className="text-primary h-8 w-auto" />
55
+ )}
56
+ <Card className="w-full">
57
+ <CardContent className="pt-6">
65
58
  <Form {...form}>
66
59
  <form
67
- className="p-6 md:p-8 flex flex-col items-stretch justify-center"
60
+ className="flex flex-col items-center gap-6"
68
61
  onSubmit={form.handleSubmit(data => onFormSubmit?.(data.username, data.password))}
69
62
  >
70
- <div className="flex flex-col gap-6">
71
- <div className="flex flex-col items-start space-y-4">
72
- {loginExtensions.logo ? (
73
- <>
74
- <loginExtensions.logo.component />
75
- {loginExtensions.beforeForm && (
76
- <>
77
- <loginExtensions.beforeForm.component />
78
- <Separator className="w-full" />
79
- </>
80
- )}
81
- </>
82
- ) : (
83
- <>
84
- {!uiConfig.hideVendureBranding && (
85
- <LogoMark className="text-vendure-brand h-6 w-auto" />
86
- )}
87
- <div>
88
- <h1 className="text-2xl font-medium">
89
- <Trans>Welcome back!</Trans>
90
- </h1>
91
- <p className="text-muted-foreground text-balance">
92
- Login to your Vendure store
93
- </p>
94
- </div>
95
- {loginExtensions.beforeForm && (
96
- <>
97
- <Separator className="w-full" />
98
- <div className="w-full">
99
- <loginExtensions.beforeForm.component />
100
- </div>
101
- </>
102
- )}
103
- </>
104
- )}
63
+ {loginExtensions.beforeForm ? (
64
+ <div className="w-full">
65
+ <loginExtensions.beforeForm.component />
66
+ </div>
67
+ ) : (
68
+ <div className="flex flex-col items-center text-center gap-2">
69
+ <h1 className="text-2xl font-semibold tracking-tight">
70
+ <Trans>Welcome to Vendure</Trans>
71
+ </h1>
72
+ <p className="text-sm text-muted-foreground">
73
+ <Trans>Sign in to access the admin dashboard</Trans>
74
+ </p>
105
75
  </div>
76
+ )}
77
+ <div className="grid gap-4 w-full">
106
78
  <FormField
107
79
  control={form.control}
108
80
  name="username"
109
81
  render={({ field }) => (
110
82
  <FormItem>
111
- <FormLabel htmlFor="email" asChild>
112
- <Trans>Username</Trans>
113
- </FormLabel>
114
83
  <FormControl>
115
- <Input {...field} placeholder="Username or email address" />
84
+ <Input {...field} placeholder={t`Email`} />
116
85
  </FormControl>
117
86
  <FormMessage />
118
87
  </FormItem>
@@ -123,84 +92,31 @@ export function LoginForm({ className, onFormSubmit, isVerifying, loginError, ..
123
92
  name="password"
124
93
  render={({ field }) => (
125
94
  <FormItem>
126
- <div className="flex items-center">
127
- <FormLabel htmlFor="password" asChild>
128
- <Trans>Password</Trans>
129
- </FormLabel>
130
- <a
131
- tabIndex={-1}
132
- href="#"
133
- className="ml-auto text-sm underline-offset-2 hover:underline"
134
- >
135
- Forgot your password?
136
- </a>
137
- </div>
138
95
  <FormControl>
139
- <Input {...field} type="password" />
96
+ <PasswordInput {...field} placeholder={t`Password`} />
140
97
  </FormControl>
141
98
  <FormMessage />
142
99
  </FormItem>
143
100
  )}
144
101
  />
145
- <Button type="submit" disabled={isVerifying}>
102
+ <Button type="submit" className="w-full" disabled={isVerifying}>
146
103
  {isVerifying && (
147
104
  <>
148
105
  <Loader2 className="animate-spin" />
149
106
  Please wait
150
107
  </>
151
108
  )}
152
- {!isVerifying && <span>Login</span>}
109
+ {!isVerifying && <Trans>Sign in</Trans>}
153
110
  </Button>
154
111
  </div>
155
112
  {loginExtensions.afterForm && (
156
113
  <>
157
- <Separator className="w-full my-4" />
158
-
114
+ <Separator className="w-full" />
159
115
  <loginExtensions.afterForm.component />
160
116
  </>
161
117
  )}
162
118
  </form>
163
119
  </Form>
164
- {loginExtensions.loginImage ? (
165
- <loginExtensions.loginImage.component />
166
- ) : (
167
- <div className="bg-muted relative hidden md:block lg:min-h-[500px]">
168
- {remoteLoginImage && (
169
- <>
170
- <img
171
- src={remoteLoginImage.urls.regular}
172
- alt="Image"
173
- className="absolute inset-0 h-full w-full object-cover"
174
- />
175
- <div className="absolute h-full w-full top-0 left-0 flex items-end justify-start bg-gradient-to-b from-transparent to-black/80 p-4 ">
176
- <div>
177
- <p className="text-lg font-medium text-white">
178
- {remoteLoginImage.location.name}
179
- </p>
180
- <p className="text-sm text-white/80">
181
- By
182
- <a
183
- className="mx-1 underline"
184
- href={remoteLoginImage.user.links.html}
185
- target="_blank"
186
- >
187
- {remoteLoginImage.user.name}
188
- </a>
189
- on Unsplash
190
- </p>
191
- </div>
192
- </div>
193
- </>
194
- )}
195
- {uiConfig.loginImageUrl && (
196
- <img
197
- src={uiConfig.loginImageUrl}
198
- alt="Login image"
199
- className="absolute inset-0 h-full w-full object-cover"
200
- />
201
- )}
202
- </div>
203
- )}
204
120
  </CardContent>
205
121
  </Card>
206
122
  </div>
@@ -1,38 +1,50 @@
1
- import { useAlerts } from '@/vdb/framework/alert/alert-extensions.js';
2
1
  import { AlertItem } from '@/vdb/framework/alert/alert-item.js';
3
2
  import { AlertsIndicator } from '@/vdb/framework/alert/alerts-indicator.js';
3
+ import { useAlerts } from '@/vdb/hooks/use-alerts.js';
4
+ import { Trans } from '@lingui/react/macro';
4
5
  import { BellIcon } from 'lucide-react';
5
6
  import { Button } from '../ui/button.js';
6
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '../ui/dialog.js';
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuLabel,
11
+ DropdownMenuSeparator,
12
+ DropdownMenuTrigger,
13
+ } from '../ui/dropdown-menu.js';
7
14
  import { ScrollArea } from '../ui/scroll-area.js';
8
15
 
9
16
  export function Alerts() {
10
- const { alerts } = useAlerts();
17
+ const { alerts, activeCount } = useAlerts();
11
18
 
12
19
  if (alerts.length === 0) {
13
20
  return null;
14
21
  }
15
22
 
16
23
  return (
17
- <Dialog>
18
- <DialogTrigger asChild>
24
+ <DropdownMenu>
25
+ <DropdownMenuTrigger asChild>
19
26
  <Button size="icon" variant="ghost" className="relative">
20
27
  <BellIcon />
21
28
  <AlertsIndicator />
22
29
  </Button>
23
- </DialogTrigger>
24
- <DialogContent>
25
- <DialogHeader>
26
- <DialogTitle>Alerts</DialogTitle>
27
- </DialogHeader>
30
+ </DropdownMenuTrigger>
31
+ <DropdownMenuContent align="end" className="max-w-[800px] min-w-96">
32
+ <DropdownMenuLabel>Alerts</DropdownMenuLabel>
33
+ <DropdownMenuSeparator />
28
34
  <ScrollArea className="max-h-[500px]">
29
- <div className="flex flex-col divide-y divide-border">
30
- {alerts.map(alert => (
31
- <AlertItem className="py-2" key={alert.id} alert={alert} />
32
- ))}
33
- </div>
35
+ {activeCount > 0 ? (
36
+ <div className="flex flex-col divide-y divide-border px-2">
37
+ {alerts.map(alert => (
38
+ <AlertItem className="py-2" key={alert.definition.id} alert={alert} />
39
+ ))}
40
+ </div>
41
+ ) : (
42
+ <div className="flex items-center justify-center py-10 text-muted-foreground">
43
+ <Trans>No alerts</Trans>
44
+ </div>
45
+ )}
34
46
  </ScrollArea>
35
- </DialogContent>
36
- </Dialog>
47
+ </DropdownMenuContent>
48
+ </DropdownMenu>
37
49
  );
38
50
  }
@@ -40,7 +40,7 @@ export function AssetBulkActions({ selection, bulkActions, refetch }: Readonly<A
40
40
 
41
41
  const { position, shouldShow } = useFloatingBulkActions({
42
42
  selectionCount: selection.length,
43
- containerSelector: '[data-asset-gallery]'
43
+ containerSelector: '[data-asset-gallery]',
44
44
  });
45
45
 
46
46
  if (!shouldShow) {
@@ -70,12 +70,12 @@ export function AssetBulkActions({ selection, bulkActions, refetch }: Readonly<A
70
70
 
71
71
  return (
72
72
  <div
73
- className="flex items-center gap-4 px-8 py-2 animate-in fade-in duration-200 fixed transform -translate-x-1/2 bg-white shadow-2xl rounded-md border z-50"
73
+ className="flex items-center gap-4 px-8 py-2 animate-in fade-in duration-200 fixed transform -translate-x-1/2 bg-background shadow-2xl rounded-md border z-50"
74
74
  style={{
75
75
  height: 'auto',
76
76
  maxHeight: '60px',
77
77
  bottom: position.bottom,
78
- left: position.left
78
+ left: position.left,
79
79
  }}
80
80
  >
81
81
  <span className="text-sm text-muted-foreground">
@@ -0,0 +1,76 @@
1
+ import { AssetGallery } from '@/vdb/components/shared/asset/asset-gallery.js';
2
+ import { FullWidthPageBlock, Page, PageLayout } from '@/vdb/framework/layout-engine/page-layout.js';
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+ import { RouterContextProvider } from '@tanstack/react-router';
5
+ import { createDemoRoute } from '../../../../../.storybook/providers.js';
6
+ import { withDescription } from '../../../../../.storybook/with-description.js';
7
+
8
+ const meta = {
9
+ title: 'Components/AssetGallery',
10
+ component: AssetGallery,
11
+ ...withDescription(import.meta.url, './asset-gallery.js'),
12
+ parameters: {
13
+ layout: 'fullscreen',
14
+ },
15
+ tags: ['autodocs'],
16
+ argTypes: {
17
+ selectable: {
18
+ control: 'boolean',
19
+ description: 'Whether assets can be selected',
20
+ },
21
+ multiSelect: {
22
+ control: 'select',
23
+ options: ['auto', 'manual'],
24
+ description: 'Multi-select mode',
25
+ },
26
+ pageSize: {
27
+ control: 'number',
28
+ description: 'Number of assets per page',
29
+ },
30
+ fixedHeight: {
31
+ control: 'boolean',
32
+ description: 'Whether the gallery has a fixed height',
33
+ },
34
+ showHeader: {
35
+ control: 'boolean',
36
+ description: 'Whether to show the header with search and filters',
37
+ },
38
+ displayBulkActions: {
39
+ control: 'boolean',
40
+ description: 'Whether to display bulk actions bar',
41
+ },
42
+ },
43
+ } satisfies Meta<typeof AssetGallery>;
44
+
45
+ export default meta;
46
+ type Story = StoryObj<typeof meta>;
47
+
48
+ export const Playground: Story = {
49
+ args: {
50
+ selectable: true,
51
+ multiSelect: 'manual',
52
+ pageSize: 12,
53
+ fixedHeight: false,
54
+ showHeader: true,
55
+ displayBulkActions: true,
56
+ },
57
+ render: args => {
58
+ const { route, router } = createDemoRoute();
59
+ return (
60
+ <div className="p-6 h-screen">
61
+ <RouterContextProvider router={router}>
62
+ <Page pageId="test-page">
63
+ <PageLayout>
64
+ <FullWidthPageBlock blockId="test-block">
65
+ <AssetGallery
66
+ {...args}
67
+ onSelect={assets => console.log('Selected:', assets)}
68
+ />
69
+ </FullWidthPageBlock>
70
+ </PageLayout>
71
+ </Page>
72
+ </RouterContextProvider>
73
+ </div>
74
+ );
75
+ },
76
+ };