@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.
- 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.tsx +2 -0
- 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/_product-variants/product-variants.tsx +2 -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-form-input.stories.tsx +65 -0
- package/src/lib/components/data-input/{password-input.tsx → password-form-input.tsx} +1 -1
- 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 +29 -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 +8 -3
- 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 +14 -1
- package/src/lib/providers/alerts-provider.tsx +60 -0
- package/src/lib/providers/theme-provider.tsx +6 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Bold, Italic, Underline } from 'lucide-react';
|
|
3
|
+
import { ToggleGroup, ToggleGroupItem } from './toggle-group.js';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'UI/Toggle Group',
|
|
7
|
+
component: ToggleGroup,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
} satisfies Meta<typeof ToggleGroup>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Playground: Story = {
|
|
18
|
+
render: () => (
|
|
19
|
+
<ToggleGroup type="multiple">
|
|
20
|
+
<ToggleGroupItem value="bold" aria-label="Toggle bold">
|
|
21
|
+
<Bold className="h-4 w-4" />
|
|
22
|
+
</ToggleGroupItem>
|
|
23
|
+
<ToggleGroupItem value="italic" aria-label="Toggle italic">
|
|
24
|
+
<Italic className="h-4 w-4" />
|
|
25
|
+
</ToggleGroupItem>
|
|
26
|
+
<ToggleGroupItem value="underline" aria-label="Toggle underline">
|
|
27
|
+
<Underline className="h-4 w-4" />
|
|
28
|
+
</ToggleGroupItem>
|
|
29
|
+
</ToggleGroup>
|
|
30
|
+
),
|
|
31
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Bold } from 'lucide-react';
|
|
3
|
+
import { Toggle } from './toggle.js';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'UI/Toggle',
|
|
7
|
+
component: Toggle,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
argTypes: {
|
|
13
|
+
variant: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['default', 'outline'],
|
|
16
|
+
description: 'Toggle variant',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['default', 'sm', 'lg'],
|
|
21
|
+
description: 'Toggle size',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
} satisfies Meta<typeof Toggle>;
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
export const Playground: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
variant: 'default',
|
|
32
|
+
size: 'default',
|
|
33
|
+
},
|
|
34
|
+
render: args => (
|
|
35
|
+
<Toggle {...args}>
|
|
36
|
+
<Bold className="h-4 w-4" />
|
|
37
|
+
</Toggle>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Button } from './button.js';
|
|
3
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip.js';
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'UI/Tooltip',
|
|
7
|
+
component: Tooltip,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
},
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
} satisfies Meta<typeof Tooltip>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Playground: Story = {
|
|
18
|
+
render: () => (
|
|
19
|
+
<TooltipProvider>
|
|
20
|
+
<Tooltip>
|
|
21
|
+
<TooltipTrigger asChild>
|
|
22
|
+
<Button>Hover me</Button>
|
|
23
|
+
</TooltipTrigger>
|
|
24
|
+
<TooltipContent>
|
|
25
|
+
<p>This is a tooltip</p>
|
|
26
|
+
</TooltipContent>
|
|
27
|
+
</Tooltip>
|
|
28
|
+
</TooltipProvider>
|
|
29
|
+
),
|
|
30
|
+
};
|
|
@@ -36,13 +36,13 @@ function TooltipContent({
|
|
|
36
36
|
data-slot="tooltip-content"
|
|
37
37
|
sideOffset={sideOffset}
|
|
38
38
|
className={cn(
|
|
39
|
-
'bg-
|
|
39
|
+
'bg-secondary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-w-sm rounded-md px-3 py-1.5 text-xs',
|
|
40
40
|
className,
|
|
41
41
|
)}
|
|
42
42
|
{...props}
|
|
43
43
|
>
|
|
44
44
|
{children}
|
|
45
|
-
<TooltipPrimitive.Arrow className="bg-
|
|
45
|
+
<TooltipPrimitive.Arrow className="bg-secondary fill-secondary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
46
46
|
</TooltipPrimitive.Content>
|
|
47
47
|
</TooltipPrimitive.Portal>
|
|
48
48
|
);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
1
|
import { DashboardAlertDefinition } from '../extension-api/types/alerts.js';
|
|
3
2
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
4
3
|
|
|
@@ -18,13 +17,3 @@ export function getAlertRegistry() {
|
|
|
18
17
|
export function getAlert(id: string) {
|
|
19
18
|
return getAlertRegistry().get(id);
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
export function useAlerts() {
|
|
23
|
-
const [alerts, setAlerts] = useState<DashboardAlertDefinition[]>([]);
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
setAlerts(Array.from(getAlertRegistry().values()));
|
|
27
|
-
}, []);
|
|
28
|
-
|
|
29
|
-
return { alerts };
|
|
30
|
-
}
|
|
@@ -1,44 +1,39 @@
|
|
|
1
1
|
import { Button } from '@/vdb/components/ui/button.js';
|
|
2
|
+
import { AlertEntry } from '@/vdb/hooks/use-alerts.js';
|
|
2
3
|
import { cn } from '@/vdb/lib/utils.js';
|
|
3
|
-
import { useQuery } from '@tanstack/react-query';
|
|
4
4
|
import { ComponentProps } from 'react';
|
|
5
5
|
|
|
6
|
-
import { DashboardAlertDefinition } from '../extension-api/types/alerts.js';
|
|
7
|
-
|
|
8
6
|
interface AlertItemProps extends ComponentProps<'div'> {
|
|
9
|
-
alert:
|
|
7
|
+
alert: AlertEntry;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
export function AlertItem({ alert, className, ...props }: Readonly<AlertItemProps>) {
|
|
13
|
-
|
|
14
|
-
queryKey: ['alert', alert.id],
|
|
15
|
-
queryFn: () => alert.check(),
|
|
16
|
-
refetchInterval: alert.recheckInterval,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const isAlertActive = alert.shouldShow?.(data);
|
|
20
|
-
|
|
21
|
-
if (!isAlertActive) {
|
|
11
|
+
if (!alert.active) {
|
|
22
12
|
return null;
|
|
23
13
|
}
|
|
14
|
+
const { definition: def } = alert;
|
|
24
15
|
|
|
25
16
|
return (
|
|
26
17
|
<div className={cn('flex items-center justify-between gap-1', className)} {...props}>
|
|
27
18
|
<div className="flex flex-col">
|
|
28
|
-
<span className="
|
|
29
|
-
{typeof
|
|
19
|
+
<span className="text-sm">
|
|
20
|
+
{typeof def.title === 'string' ? def.title : def.title(alert.lastData)}
|
|
30
21
|
</span>
|
|
31
|
-
<span className="text-
|
|
32
|
-
{typeof
|
|
22
|
+
<span className="text-xs text-muted-foreground">
|
|
23
|
+
{typeof def.description === 'string'
|
|
24
|
+
? def.description
|
|
25
|
+
: def.description?.(alert.lastData)}
|
|
33
26
|
</span>
|
|
34
27
|
</div>
|
|
35
28
|
<div className="flex items-center gap-1">
|
|
36
|
-
{
|
|
29
|
+
{def.actions?.map(action => (
|
|
37
30
|
<Button
|
|
38
31
|
key={action.label}
|
|
39
32
|
variant="secondary"
|
|
40
33
|
size="sm"
|
|
41
|
-
onClick={() =>
|
|
34
|
+
onClick={async () => {
|
|
35
|
+
await action.onClick({ data: alert.lastData, dismiss: () => alert.dismiss() });
|
|
36
|
+
}}
|
|
42
37
|
>
|
|
43
38
|
{action.label}
|
|
44
39
|
</Button>
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useAlerts } from '@/vdb/hooks/use-alerts.js';
|
|
2
|
+
import { cn } from '@/vdb/lib/utils.js';
|
|
3
3
|
|
|
4
4
|
export function AlertsIndicator() {
|
|
5
|
-
const {
|
|
5
|
+
const { activeCount, highestSeverity } = useAlerts();
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
queryFn: () => alert.check(),
|
|
11
|
-
})),
|
|
12
|
-
combine: results => {
|
|
13
|
-
return results.filter((result, idx) => result.data && alerts[idx].shouldShow?.(result.data))
|
|
14
|
-
.length;
|
|
15
|
-
},
|
|
16
|
-
});
|
|
7
|
+
if (activeCount === 0) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
17
10
|
|
|
18
11
|
return (
|
|
19
|
-
<div
|
|
20
|
-
{
|
|
12
|
+
<div
|
|
13
|
+
className={cn(
|
|
14
|
+
`absolute -right-1 -top-1 rounded-full bg-primary text-xs w-4 h-4 flex items-center justify-center`,
|
|
15
|
+
highestSeverity === 'error' && 'bg-destructive',
|
|
16
|
+
highestSeverity === 'warning' && 'bg-yellow-400 dark:bg-yellow-600',
|
|
17
|
+
)}
|
|
18
|
+
>
|
|
19
|
+
{activeCount}
|
|
21
20
|
</div>
|
|
22
21
|
);
|
|
23
22
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { api } from '@/vdb/graphql/api.js';
|
|
2
|
+
import { graphql } from '@/vdb/graphql/graphql.js';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
|
|
5
|
+
import { DashboardAlertDefinition } from '../../extension-api/types/index.js';
|
|
6
|
+
|
|
7
|
+
const pendingSearchIndexUpdatesDocument = graphql(`
|
|
8
|
+
query GetPendingSearchIndexUpdates {
|
|
9
|
+
pendingSearchIndexUpdates
|
|
10
|
+
}
|
|
11
|
+
`);
|
|
12
|
+
|
|
13
|
+
export const runPendingSearchIndexUpdatesDocument = graphql(`
|
|
14
|
+
mutation RunPendingSearchIndexUpdates {
|
|
15
|
+
runPendingSearchIndexUpdates {
|
|
16
|
+
success
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
`);
|
|
20
|
+
|
|
21
|
+
export const searchIndexBufferAlert: DashboardAlertDefinition<number> = {
|
|
22
|
+
id: 'search-index-buffer-alert',
|
|
23
|
+
check: async () => {
|
|
24
|
+
const data = await api.query(pendingSearchIndexUpdatesDocument);
|
|
25
|
+
return data.pendingSearchIndexUpdates;
|
|
26
|
+
},
|
|
27
|
+
shouldShow: data => data > 0,
|
|
28
|
+
title: data => /* i18n*/ `${data} pending search index updates`,
|
|
29
|
+
severity: data => (data < 10 ? 'info' : 'warning'),
|
|
30
|
+
actions: [
|
|
31
|
+
{
|
|
32
|
+
label: /* i18n*/ `Run pending updates`,
|
|
33
|
+
onClick: async ({ dismiss }) => {
|
|
34
|
+
await api.mutate(runPendingSearchIndexUpdatesDocument, {});
|
|
35
|
+
toast.success(/* i18n*/ 'Running pending search index updates');
|
|
36
|
+
dismiss();
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
recheckInterval: 60_000,
|
|
41
|
+
};
|
|
@@ -1,24 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DashboardFormComponent,
|
|
3
|
-
DashboardFormComponentProps,
|
|
4
|
-
} from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
1
|
+
import { DashboardFormComponent } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
5
2
|
import * as React from 'react';
|
|
6
3
|
import { getDisplayComponent } from '../extension-api/display-component-extensions.js';
|
|
7
4
|
import { getInputComponent } from '../extension-api/input-component-extensions.js';
|
|
8
5
|
|
|
9
|
-
export
|
|
10
|
-
component: React.ComponentType<Props>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Display component interface (unchanged)
|
|
14
|
-
export interface DataDisplayComponentProps {
|
|
6
|
+
export type DataDisplayComponentProps<T extends Record<string, any> = Record<string, any>> = {
|
|
15
7
|
value: any;
|
|
16
|
-
|
|
17
|
-
[key: string]: any;
|
|
18
|
-
}
|
|
8
|
+
} & T;
|
|
19
9
|
|
|
20
10
|
export type DataDisplayComponent = React.ComponentType<DataDisplayComponentProps>;
|
|
21
|
-
export type { DashboardFormComponentProps as DataInputComponentProps };
|
|
22
11
|
|
|
23
12
|
// Component registry hook that uses the global registry
|
|
24
13
|
export function useComponentRegistry() {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/vdb/components/ui/card.js';
|
|
2
2
|
import { DashboardBaseWidgetProps } from '@/vdb/framework/extension-api/types/index.js';
|
|
3
|
-
import { Trans } from '@lingui/react/macro';
|
|
4
|
-
import { useLingui } from '@lingui/react/macro';
|
|
5
3
|
import { cn } from '@/vdb/lib/utils.js';
|
|
4
|
+
import { Trans, useLingui } from '@lingui/react/macro';
|
|
6
5
|
import { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
7
6
|
|
|
8
7
|
type WidgetDimensions = {
|
|
@@ -21,14 +20,24 @@ export const useWidgetDimensions = () => {
|
|
|
21
20
|
return context;
|
|
22
21
|
};
|
|
23
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @description
|
|
25
|
+
* A wrapper component that should be used for any custom Insights page widgets.
|
|
26
|
+
* This ensures that your custom widget has all the basic functionality needed to be
|
|
27
|
+
* correctly rendered on the Insights page.
|
|
28
|
+
*
|
|
29
|
+
* @docsCategory extensions-api
|
|
30
|
+
* @docsPage widgets
|
|
31
|
+
* @since 3.3.0
|
|
32
|
+
*/
|
|
24
33
|
export function DashboardBaseWidget({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
id,
|
|
35
|
+
config,
|
|
36
|
+
children,
|
|
37
|
+
title,
|
|
38
|
+
description,
|
|
39
|
+
actions,
|
|
40
|
+
}: DashboardBaseWidgetProps) {
|
|
32
41
|
const headerRef = useRef<HTMLDivElement>(null);
|
|
33
42
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
34
43
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useLingui } from '@lingui/react/macro';
|
|
4
|
-
import { createContext,
|
|
4
|
+
import { createContext, PropsWithChildren, useContext } from 'react';
|
|
5
5
|
|
|
6
6
|
export interface DefinedDateRange {
|
|
7
7
|
from: Date;
|
|
@@ -14,17 +14,18 @@ export interface WidgetFilters {
|
|
|
14
14
|
|
|
15
15
|
const WidgetFiltersContext = createContext<WidgetFilters | undefined>(undefined);
|
|
16
16
|
|
|
17
|
-
export function WidgetFiltersProvider({
|
|
18
|
-
children
|
|
19
|
-
filters
|
|
20
|
-
}: PropsWithChildren<{ filters: WidgetFilters }>) {
|
|
21
|
-
return (
|
|
22
|
-
<WidgetFiltersContext.Provider value={filters}>
|
|
23
|
-
{children}
|
|
24
|
-
</WidgetFiltersContext.Provider>
|
|
25
|
-
);
|
|
17
|
+
export function WidgetFiltersProvider({ children, filters }: PropsWithChildren<{ filters: WidgetFilters }>) {
|
|
18
|
+
return <WidgetFiltersContext.Provider value={filters}>{children}</WidgetFiltersContext.Provider>;
|
|
26
19
|
}
|
|
27
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* Exposes a context object for use in building Insights page widgets.
|
|
24
|
+
*
|
|
25
|
+
* @docsCategory hooks
|
|
26
|
+
* @docsPage useWidgetFilters
|
|
27
|
+
* @since 3.5.0
|
|
28
|
+
*/
|
|
28
29
|
export function useWidgetFilters() {
|
|
29
30
|
const { t } = useLingui();
|
|
30
31
|
const context = useContext(WidgetFiltersContext);
|
|
@@ -32,4 +33,4 @@ export function useWidgetFilters() {
|
|
|
32
33
|
throw new Error(t`useWidgetFilters must be used within a WidgetFiltersProvider`);
|
|
33
34
|
}
|
|
34
35
|
return context;
|
|
35
|
-
}
|
|
36
|
+
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
+
import { registerAlert } from '@/vdb/framework/alert/alert-extensions.js';
|
|
2
|
+
import { searchIndexBufferAlert } from '@/vdb/framework/alert/search-index-buffer-alert/search-index-buffer-alert.js';
|
|
1
3
|
import { setNavMenuConfig } from '@/vdb/framework/nav-menu/nav-menu-extensions.js';
|
|
2
|
-
import {
|
|
3
|
-
LayoutDashboardIcon,
|
|
4
|
-
Mail,
|
|
5
|
-
Settings2,
|
|
6
|
-
ShoppingCart,
|
|
7
|
-
SquareTerminal,
|
|
8
|
-
Terminal,
|
|
9
|
-
Users,
|
|
10
|
-
} from 'lucide-react';
|
|
4
|
+
import { ChartLine, Percent, Settings2, ShoppingBag, Tags, Terminal, Users } from 'lucide-react';
|
|
11
5
|
|
|
12
6
|
import { LatestOrdersWidget } from './dashboard-widget/latest-orders-widget/index.js';
|
|
13
7
|
import { MetricsWidget } from './dashboard-widget/metrics-widget/index.js';
|
|
@@ -21,14 +15,14 @@ export function registerDefaults() {
|
|
|
21
15
|
id: 'insights',
|
|
22
16
|
title: /* i18n*/ 'Insights',
|
|
23
17
|
placement: 'top',
|
|
24
|
-
icon:
|
|
18
|
+
icon: ChartLine,
|
|
25
19
|
url: '/',
|
|
26
20
|
order: 100,
|
|
27
21
|
},
|
|
28
22
|
{
|
|
29
23
|
id: 'catalog',
|
|
30
24
|
title: /* i18n*/ 'Catalog',
|
|
31
|
-
icon:
|
|
25
|
+
icon: Tags,
|
|
32
26
|
placement: 'top',
|
|
33
27
|
order: 200,
|
|
34
28
|
items: [
|
|
@@ -72,7 +66,7 @@ export function registerDefaults() {
|
|
|
72
66
|
{
|
|
73
67
|
id: 'sales',
|
|
74
68
|
title: /* i18n*/ 'Sales',
|
|
75
|
-
icon:
|
|
69
|
+
icon: ShoppingBag,
|
|
76
70
|
placement: 'top',
|
|
77
71
|
order: 300,
|
|
78
72
|
items: [
|
|
@@ -111,7 +105,7 @@ export function registerDefaults() {
|
|
|
111
105
|
{
|
|
112
106
|
id: 'marketing',
|
|
113
107
|
title: /* i18n*/ 'Marketing',
|
|
114
|
-
icon:
|
|
108
|
+
icon: Percent,
|
|
115
109
|
placement: 'top',
|
|
116
110
|
order: 500,
|
|
117
111
|
items: [
|
|
@@ -271,4 +265,6 @@ export function registerDefaults() {
|
|
|
271
265
|
component: OrdersSummaryWidget,
|
|
272
266
|
defaultSize: { w: 6, h: 3, x: 6, y: 0 },
|
|
273
267
|
});
|
|
268
|
+
|
|
269
|
+
registerAlert(searchIndexBufferAlert);
|
|
274
270
|
}
|
|
@@ -8,13 +8,18 @@ import {
|
|
|
8
8
|
RichTextInput,
|
|
9
9
|
SelectWithOptions,
|
|
10
10
|
} from '@/vdb/components/data-input/index.js';
|
|
11
|
-
import {
|
|
11
|
+
import { PasswordFormInput } from '@/vdb/components/data-input/password-form-input.js';
|
|
12
12
|
import { TextareaInput } from '@/vdb/components/data-input/textarea-input.js';
|
|
13
13
|
import { DashboardFormComponent } from '@/vdb/framework/form-engine/form-engine-types.js';
|
|
14
14
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
15
15
|
|
|
16
16
|
globalRegistry.register('inputComponents', new Map<string, DashboardFormComponent>());
|
|
17
17
|
|
|
18
|
+
const DefaultProductInput: DashboardFormComponent = props => (
|
|
19
|
+
<DefaultRelationInput {...props} entityType="ProductVariant" />
|
|
20
|
+
);
|
|
21
|
+
DefaultProductInput.metadata = { isListInput: 'dynamic' };
|
|
22
|
+
|
|
18
23
|
// Register built-in input components
|
|
19
24
|
const inputComponents = globalRegistry.get('inputComponents');
|
|
20
25
|
inputComponents.set('facet-value-input', FacetValueInput);
|
|
@@ -27,8 +32,8 @@ inputComponents.set('json-editor-form-input', TextareaInput);
|
|
|
27
32
|
inputComponents.set('textarea-form-input', TextareaInput);
|
|
28
33
|
inputComponents.set('html-editor-form-input', RichTextInput);
|
|
29
34
|
inputComponents.set('rich-text-form-input', RichTextInput);
|
|
30
|
-
inputComponents.set('password-form-input',
|
|
31
|
-
inputComponents.set('product-selector-form-input',
|
|
35
|
+
inputComponents.set('password-form-input', PasswordFormInput);
|
|
36
|
+
inputComponents.set('product-selector-form-input', DefaultProductInput);
|
|
32
37
|
inputComponents.set('relation-form-input', DefaultRelationInput);
|
|
33
38
|
inputComponents.set('select-form-input', SelectWithOptions);
|
|
34
39
|
inputComponents.set('product-multi-form-input', ProductMultiInput);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { registerAlert } from '@/vdb/framework/alert/alert-extensions.js';
|
|
2
|
+
|
|
2
3
|
import { DashboardAlertDefinition } from '../types/alerts.js';
|
|
3
4
|
|
|
4
5
|
export function registerAlertExtensions(alerts?: DashboardAlertDefinition[]) {
|
|
5
6
|
if (alerts) {
|
|
6
7
|
for (const alert of alerts) {
|
|
7
|
-
|
|
8
|
+
registerAlert(alert);
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type AlertSeverity = 'info' | 'warning' | 'error';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @description
|
|
3
5
|
* Allows you to define custom alerts that can be displayed in the dashboard.
|
|
@@ -26,7 +28,7 @@ export interface DashboardAlertDefinition<TResponse = any> {
|
|
|
26
28
|
* @description
|
|
27
29
|
* The severity level of the alert.
|
|
28
30
|
*/
|
|
29
|
-
severity:
|
|
31
|
+
severity: AlertSeverity | ((data: TResponse) => AlertSeverity);
|
|
30
32
|
/**
|
|
31
33
|
* @description
|
|
32
34
|
* A function that checks the condition and returns the response data.
|
|
@@ -34,20 +36,24 @@ export interface DashboardAlertDefinition<TResponse = any> {
|
|
|
34
36
|
check: () => Promise<TResponse> | TResponse;
|
|
35
37
|
/**
|
|
36
38
|
* @description
|
|
37
|
-
*
|
|
39
|
+
* A function that determines whether the alert should be rendered based on the response data.
|
|
38
40
|
*/
|
|
39
|
-
|
|
41
|
+
shouldShow: (data: TResponse) => boolean;
|
|
40
42
|
/**
|
|
41
43
|
* @description
|
|
42
|
-
*
|
|
44
|
+
* The interval in milliseconds to recheck the condition.
|
|
43
45
|
*/
|
|
44
|
-
|
|
46
|
+
recheckInterval?: number;
|
|
45
47
|
/**
|
|
46
48
|
* @description
|
|
47
49
|
* Optional actions that can be performed when the alert is shown.
|
|
50
|
+
*
|
|
51
|
+
* The `onClick()` handler will receive the data returned by the `check` function,
|
|
52
|
+
* as well as a `dismiss()` function that can be used to immediately dismiss the
|
|
53
|
+
* current alert.
|
|
48
54
|
*/
|
|
49
55
|
actions?: Array<{
|
|
50
56
|
label: string;
|
|
51
|
-
onClick: (data: TResponse) => void
|
|
57
|
+
onClick: (args: { data: TResponse; dismiss: () => void }) => void | Promise<any>;
|
|
52
58
|
}>;
|
|
53
59
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { DataDisplayComponentProps } from '@/vdb/framework/component-registry/component-registry.js';
|
|
1
2
|
import { Table } from '@tanstack/react-table';
|
|
3
|
+
import { CellContext } from '@tanstack/table-core';
|
|
2
4
|
import { DocumentNode } from 'graphql';
|
|
5
|
+
import React from 'react';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* @description
|
|
@@ -21,7 +24,7 @@ export interface DashboardDataTableDisplayComponent {
|
|
|
21
24
|
* The React component that will be rendered as the display.
|
|
22
25
|
* It should accept `value` and other standard display props.
|
|
23
26
|
*/
|
|
24
|
-
component: React.ComponentType<
|
|
27
|
+
component: React.ComponentType<DataDisplayComponentProps<CellContext<any, any>>>;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export type BulkActionContext<Item extends { id: string } & Record<string, any>> = {
|
|
@@ -120,7 +123,7 @@ export type BulkAction = {
|
|
|
120
123
|
* This allows you to customize aspects of existing data tables in the dashboard.
|
|
121
124
|
*
|
|
122
125
|
* @docsCategory extensions-api
|
|
123
|
-
* @docsPage
|
|
126
|
+
* @docsPage DataTables
|
|
124
127
|
* @since 3.4.0
|
|
125
128
|
*/
|
|
126
129
|
export interface DashboardDataTableExtensionDefinition {
|
|
@@ -48,22 +48,6 @@ export interface LoginAfterFormExtension {
|
|
|
48
48
|
component: React.ComponentType;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* @description
|
|
53
|
-
* Defines a custom login image component that replaces the default image panel.
|
|
54
|
-
*
|
|
55
|
-
* @docsCategory extensions-api
|
|
56
|
-
* @docsPage Login
|
|
57
|
-
* @since 3.4.0
|
|
58
|
-
*/
|
|
59
|
-
export interface LoginImageExtension {
|
|
60
|
-
/**
|
|
61
|
-
* @description
|
|
62
|
-
* A React component that will replace the default login image panel.
|
|
63
|
-
*/
|
|
64
|
-
component: React.ComponentType;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
51
|
/**
|
|
68
52
|
* @description
|
|
69
53
|
* Defines all available login page extensions.
|
|
@@ -89,9 +73,4 @@ export interface DashboardLoginExtensions {
|
|
|
89
73
|
* Component to render after the login form.
|
|
90
74
|
*/
|
|
91
75
|
afterForm?: LoginAfterFormExtension;
|
|
92
|
-
/**
|
|
93
|
-
* @description
|
|
94
|
-
* Custom login image component to replace the default image panel.
|
|
95
|
-
*/
|
|
96
|
-
loginImage?: LoginImageExtension;
|
|
97
76
|
}
|