@vendure/dashboard 3.3.6-master-202507010731 → 3.3.6-master-202507011151

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 (28) hide show
  1. package/package.json +131 -131
  2. package/src/app/common/duplicate-bulk-action.tsx +134 -0
  3. package/src/app/routes/_authenticated/_collections/collections.graphql.ts +9 -0
  4. package/src/app/routes/_authenticated/_collections/collections.tsx +10 -0
  5. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +66 -6
  6. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +4 -5
  7. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +19 -106
  8. package/src/app/routes/_authenticated/_products/products.graphql.ts +0 -17
  9. package/src/lib/components/shared/custom-fields-form.tsx +18 -1
  10. package/src/lib/components/shared/detail-page-button.tsx +1 -1
  11. package/src/lib/framework/extension-api/use-dashboard-extensions.ts +2 -1
  12. package/src/lib/framework/form-engine/use-generated-form.tsx +5 -8
  13. package/src/lib/framework/form-engine/utils.ts +43 -15
  14. package/src/lib/framework/page/use-detail-page.ts +4 -4
  15. package/src/lib/framework/page/use-extended-router.tsx +4 -5
  16. package/src/lib/graphql/common-operations.ts +18 -0
  17. package/src/lib/graphql/{fragments.tsx → fragments.ts} +1 -2
  18. package/src/lib/graphql/graphql-env.d.ts +3 -3
  19. package/src/lib/hooks/use-channel.ts +2 -1
  20. package/src/lib/hooks/use-extended-detail-query.ts +2 -1
  21. package/src/lib/hooks/use-extended-list-query.ts +3 -2
  22. package/src/lib/hooks/use-grouped-permissions.ts +4 -2
  23. package/src/lib/hooks/use-page-block.tsx +1 -1
  24. package/src/lib/hooks/use-page.tsx +2 -2
  25. package/src/lib/hooks/use-permissions.ts +3 -2
  26. package/src/lib/hooks/use-server-config.ts +2 -1
  27. package/src/lib/hooks/use-theme.ts +2 -1
  28. package/src/lib/providers/auth.tsx +34 -11
package/package.json CHANGED
@@ -1,134 +1,134 @@
1
1
  {
2
- "name": "@vendure/dashboard",
3
- "private": false,
4
- "version": "3.3.6-master-202507010731",
5
- "type": "module",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/vendure-ecommerce/vendure"
2
+ "name": "@vendure/dashboard",
3
+ "private": false,
4
+ "version": "3.3.6-master-202507011151",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/vendure-ecommerce/vendure"
9
+ },
10
+ "homepage": "https://www.vendure.io",
11
+ "funding": "https://github.com/sponsors/michaelbromley",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "dev": "vite",
17
+ "build:standalone": "vite build",
18
+ "build": "tsc --project tsconfig.plugin.json",
19
+ "watch": "tsc --project tsconfig.plugin.json --watch",
20
+ "test": "vitest run",
21
+ "lint": "eslint .",
22
+ "preview": "vite preview",
23
+ "generate-index": "node scripts/generate-index.js"
24
+ },
25
+ "module": "./src/lib/index.ts",
26
+ "main": "./src/lib/index.ts",
27
+ "types": "./src/lib/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./src/lib/index.d.ts",
31
+ "import": "./src/lib/index.ts",
32
+ "require": "./src/lib/index.ts"
9
33
  },
10
- "homepage": "https://www.vendure.io",
11
- "funding": "https://github.com/sponsors/michaelbromley",
12
- "publishConfig": {
13
- "access": "public"
14
- },
15
- "scripts": {
16
- "dev": "vite",
17
- "build:standalone": "vite build",
18
- "build": "tsc --project tsconfig.plugin.json",
19
- "watch": "tsc --project tsconfig.plugin.json --watch",
20
- "test": "vitest run",
21
- "lint": "eslint .",
22
- "preview": "vite preview",
23
- "generate-index": "node ./generate-index.js"
24
- },
25
- "module": "./src/lib/index.ts",
26
- "main": "./src/lib/index.ts",
27
- "types": "./src/lib/index.d.ts",
28
- "exports": {
29
- ".": {
30
- "types": "./src/lib/index.d.ts",
31
- "import": "./src/lib/index.ts",
32
- "require": "./src/lib/index.ts"
33
- },
34
- "./plugin": {
35
- "types": "./dist/plugin/index.d.ts",
36
- "import": "./dist/plugin/index.js",
37
- "require": "./dist/plugin/index.js"
38
- }
39
- },
40
- "files": [
41
- "dist",
42
- "src",
43
- "vite",
44
- "lingui.config.js",
45
- "index.html"
46
- ],
47
- "dependencies": {
48
- "@dnd-kit/core": "^6.3.1",
49
- "@dnd-kit/modifiers": "^9.0.0",
50
- "@dnd-kit/sortable": "^10.0.0",
51
- "@hookform/resolvers": "^4.1.3",
52
- "@lingui/babel-plugin-lingui-macro": "^5.2.0",
53
- "@lingui/cli": "^5.2.0",
54
- "@lingui/core": "^5.2.0",
55
- "@lingui/react": "^5.2.0",
56
- "@lingui/vite-plugin": "^5.2.0",
57
- "@radix-ui/react-accordion": "^1.2.3",
58
- "@radix-ui/react-alert-dialog": "^1.1.6",
59
- "@radix-ui/react-avatar": "^1.1.3",
60
- "@radix-ui/react-checkbox": "^1.1.4",
61
- "@radix-ui/react-collapsible": "^1.1.3",
62
- "@radix-ui/react-dialog": "^1.1.6",
63
- "@radix-ui/react-dropdown-menu": "^2.1.6",
64
- "@radix-ui/react-hover-card": "^1.1.6",
65
- "@radix-ui/react-label": "^2.1.2",
66
- "@radix-ui/react-popover": "^1.1.6",
67
- "@radix-ui/react-scroll-area": "^1.2.3",
68
- "@radix-ui/react-select": "^2.1.6",
69
- "@radix-ui/react-separator": "^1.1.2",
70
- "@radix-ui/react-slot": "^1.1.2",
71
- "@radix-ui/react-switch": "^1.1.3",
72
- "@radix-ui/react-tabs": "^1.1.3",
73
- "@radix-ui/react-tooltip": "^1.1.8",
74
- "@tailwindcss/vite": "^4.1.5",
75
- "@tanstack/eslint-plugin-query": "^5.66.1",
76
- "@tanstack/react-query": "^5.66.7",
77
- "@tanstack/react-query-devtools": "^5.68.0",
78
- "@tanstack/react-router": "^1.105.0",
79
- "@tanstack/react-table": "^8.21.2",
80
- "@tanstack/router-devtools": "^1.105.0",
81
- "@tanstack/router-plugin": "^1.105.0",
82
- "@tiptap/pm": "^2.11.5",
83
- "@tiptap/react": "^2.11.5",
84
- "@tiptap/starter-kit": "^2.11.5",
85
- "@types/react": "^19.0.10",
86
- "@types/react-dom": "^19.0.4",
87
- "@types/react-grid-layout": "^1.3.5",
88
- "@uidotdev/usehooks": "^2.4.1",
89
- "@vendure/common": "^3.3.6-master-202507010731",
90
- "@vendure/core": "^3.3.6-master-202507010731",
91
- "@vitejs/plugin-react": "^4.3.4",
92
- "awesome-graphql-client": "^2.1.0",
93
- "class-variance-authority": "^0.7.1",
94
- "clsx": "^2.1.1",
95
- "cmdk": "^1.0.0",
96
- "date-fns": "^3.6.0",
97
- "gql.tada": "^1.8.10",
98
- "graphql": "^16.10.0",
99
- "json-edit-react": "^1.23.1",
100
- "lucide-react": "^0.475.0",
101
- "motion": "^12.6.2",
102
- "next-themes": "^0.4.6",
103
- "react": "^19.0.0",
104
- "react-day-picker": "^9.6.7",
105
- "react-dom": "^19.0.0",
106
- "react-dropzone": "^14.3.8",
107
- "react-grid-layout": "^1.5.1",
108
- "react-hook-form": "^7.54.2",
109
- "recharts": "^2.15.1",
110
- "sonner": "^2.0.1",
111
- "tailwind-merge": "^3.2.0",
112
- "tailwindcss": "^4.1.5",
113
- "tailwindcss-animate": "^1.0.7",
114
- "tsconfig-paths": "^4.2.0",
115
- "tw-animate-css": "^1.2.9",
116
- "vite": "^6.3.5",
117
- "zod": "^3.24.2"
118
- },
119
- "devDependencies": {
120
- "@eslint/js": "^9.19.0",
121
- "@types/node": "^22.13.4",
122
- "eslint": "^9.19.0",
123
- "eslint-plugin-react": "^7.37.4",
124
- "eslint-plugin-react-hooks": "^5.0.0",
125
- "eslint-plugin-react-refresh": "^0.4.18",
126
- "globals": "^15.14.0",
127
- "vite-plugin-dts": "^4.5.3"
128
- },
129
- "optionalDependencies": {
130
- "lightningcss-linux-arm64-musl": "^1.29.3",
131
- "lightningcss-linux-x64-musl": "^1.29.1"
132
- },
133
- "gitHead": "f5da386affaa5dcc54296a51228e2a7e5d3b0f84"
34
+ "./plugin": {
35
+ "types": "./dist/plugin/index.d.ts",
36
+ "import": "./dist/plugin/index.js",
37
+ "require": "./dist/plugin/index.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "src",
43
+ "vite",
44
+ "lingui.config.js",
45
+ "index.html"
46
+ ],
47
+ "dependencies": {
48
+ "@dnd-kit/core": "^6.3.1",
49
+ "@dnd-kit/modifiers": "^9.0.0",
50
+ "@dnd-kit/sortable": "^10.0.0",
51
+ "@hookform/resolvers": "^4.1.3",
52
+ "@lingui/babel-plugin-lingui-macro": "^5.2.0",
53
+ "@lingui/cli": "^5.2.0",
54
+ "@lingui/core": "^5.2.0",
55
+ "@lingui/react": "^5.2.0",
56
+ "@lingui/vite-plugin": "^5.2.0",
57
+ "@radix-ui/react-accordion": "^1.2.3",
58
+ "@radix-ui/react-alert-dialog": "^1.1.6",
59
+ "@radix-ui/react-avatar": "^1.1.3",
60
+ "@radix-ui/react-checkbox": "^1.1.4",
61
+ "@radix-ui/react-collapsible": "^1.1.3",
62
+ "@radix-ui/react-dialog": "^1.1.6",
63
+ "@radix-ui/react-dropdown-menu": "^2.1.6",
64
+ "@radix-ui/react-hover-card": "^1.1.6",
65
+ "@radix-ui/react-label": "^2.1.2",
66
+ "@radix-ui/react-popover": "^1.1.6",
67
+ "@radix-ui/react-scroll-area": "^1.2.3",
68
+ "@radix-ui/react-select": "^2.1.6",
69
+ "@radix-ui/react-separator": "^1.1.2",
70
+ "@radix-ui/react-slot": "^1.1.2",
71
+ "@radix-ui/react-switch": "^1.1.3",
72
+ "@radix-ui/react-tabs": "^1.1.3",
73
+ "@radix-ui/react-tooltip": "^1.1.8",
74
+ "@tailwindcss/vite": "^4.1.5",
75
+ "@tanstack/eslint-plugin-query": "^5.66.1",
76
+ "@tanstack/react-query": "^5.66.7",
77
+ "@tanstack/react-query-devtools": "^5.68.0",
78
+ "@tanstack/react-router": "^1.105.0",
79
+ "@tanstack/react-table": "^8.21.2",
80
+ "@tanstack/router-devtools": "^1.105.0",
81
+ "@tanstack/router-plugin": "^1.105.0",
82
+ "@tiptap/pm": "^2.11.5",
83
+ "@tiptap/react": "^2.11.5",
84
+ "@tiptap/starter-kit": "^2.11.5",
85
+ "@types/react": "^19.0.10",
86
+ "@types/react-dom": "^19.0.4",
87
+ "@types/react-grid-layout": "^1.3.5",
88
+ "@uidotdev/usehooks": "^2.4.1",
89
+ "@vendure/common": "^3.3.6-master-202507011151",
90
+ "@vendure/core": "^3.3.6-master-202507011151",
91
+ "@vitejs/plugin-react": "^4.3.4",
92
+ "awesome-graphql-client": "^2.1.0",
93
+ "class-variance-authority": "^0.7.1",
94
+ "clsx": "^2.1.1",
95
+ "cmdk": "^1.0.0",
96
+ "date-fns": "^3.6.0",
97
+ "gql.tada": "^1.8.10",
98
+ "graphql": "^16.10.0",
99
+ "json-edit-react": "^1.23.1",
100
+ "lucide-react": "^0.475.0",
101
+ "motion": "^12.6.2",
102
+ "next-themes": "^0.4.6",
103
+ "react": "^19.0.0",
104
+ "react-day-picker": "^9.6.7",
105
+ "react-dom": "^19.0.0",
106
+ "react-dropzone": "^14.3.8",
107
+ "react-grid-layout": "^1.5.1",
108
+ "react-hook-form": "^7.54.2",
109
+ "recharts": "^2.15.1",
110
+ "sonner": "^2.0.1",
111
+ "tailwind-merge": "^3.2.0",
112
+ "tailwindcss": "^4.1.5",
113
+ "tailwindcss-animate": "^1.0.7",
114
+ "tsconfig-paths": "^4.2.0",
115
+ "tw-animate-css": "^1.2.9",
116
+ "vite": "^6.3.5",
117
+ "zod": "^3.24.2"
118
+ },
119
+ "devDependencies": {
120
+ "@eslint/js": "^9.19.0",
121
+ "@types/node": "^22.13.4",
122
+ "eslint": "^9.19.0",
123
+ "eslint-plugin-react": "^7.37.4",
124
+ "eslint-plugin-react-hooks": "^5.0.0",
125
+ "eslint-plugin-react-refresh": "^0.4.18",
126
+ "globals": "^15.14.0",
127
+ "vite-plugin-dts": "^4.5.3"
128
+ },
129
+ "optionalDependencies": {
130
+ "lightningcss-linux-arm64-musl": "^1.29.3",
131
+ "lightningcss-linux-x64-musl": "^1.29.1"
132
+ },
133
+ "gitHead": "a707c76b649dd13d245fec107e4c4c7ccc23866b"
134
134
  }
@@ -0,0 +1,134 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import { CopyIcon } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { toast } from 'sonner';
5
+
6
+ import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
7
+ import { api } from '@/graphql/api.js';
8
+ import { duplicateEntityDocument } from '@/graphql/common-operations.js';
9
+ import { usePaginatedList } from '@/index.js';
10
+ import { Trans, useLingui } from '@/lib/trans.js';
11
+
12
+ interface DuplicateBulkActionProps {
13
+ entityType: 'Product' | 'Collection';
14
+ duplicatorCode: string;
15
+ duplicatorArguments?: Array<{ name: string; value: string }>;
16
+ requiredPermissions: string[];
17
+ entityName: string; // For display purposes in error messages
18
+ onSuccess?: () => void;
19
+ selection: any[];
20
+ table: any;
21
+ }
22
+
23
+ export function DuplicateBulkAction({
24
+ entityType,
25
+ duplicatorCode,
26
+ duplicatorArguments = [],
27
+ requiredPermissions,
28
+ entityName,
29
+ onSuccess,
30
+ selection,
31
+ table,
32
+ }: DuplicateBulkActionProps) {
33
+ const { refetchPaginatedList } = usePaginatedList();
34
+ const { i18n } = useLingui();
35
+ const [isDuplicating, setIsDuplicating] = useState(false);
36
+ const [progress, setProgress] = useState({ completed: 0, total: 0 });
37
+
38
+ const { mutateAsync } = useMutation({
39
+ mutationFn: api.mutate(duplicateEntityDocument),
40
+ });
41
+
42
+ const handleDuplicate = async () => {
43
+ if (isDuplicating) return;
44
+
45
+ setIsDuplicating(true);
46
+ setProgress({ completed: 0, total: selection.length });
47
+
48
+ const results = {
49
+ success: 0,
50
+ failed: 0,
51
+ errors: [] as string[],
52
+ };
53
+
54
+ try {
55
+ // Process entities sequentially to avoid overwhelming the server
56
+ for (let i = 0; i < selection.length; i++) {
57
+ const entity = selection[i];
58
+
59
+ try {
60
+ const result = await mutateAsync({
61
+ input: {
62
+ entityName: entityType,
63
+ entityId: entity.id,
64
+ duplicatorInput: {
65
+ code: duplicatorCode,
66
+ arguments: duplicatorArguments,
67
+ },
68
+ },
69
+ });
70
+
71
+ if ('newEntityId' in result.duplicateEntity) {
72
+ results.success++;
73
+ } else {
74
+ results.failed++;
75
+ const errorMsg =
76
+ result.duplicateEntity.message ||
77
+ result.duplicateEntity.duplicationError ||
78
+ 'Unknown error';
79
+ results.errors.push(`${entityName} ${entity.name || entity.id}: ${errorMsg}`);
80
+ }
81
+ } catch (error) {
82
+ results.failed++;
83
+ results.errors.push(
84
+ `${entityName} ${entity.name || entity.id}: ${error instanceof Error ? error.message : 'Unknown error'}`,
85
+ );
86
+ }
87
+
88
+ setProgress({ completed: i + 1, total: selection.length });
89
+ }
90
+
91
+ // Show results
92
+ if (results.success > 0) {
93
+ toast.success(
94
+ i18n.t(`Successfully duplicated ${results.success} ${entityName.toLowerCase()}s`),
95
+ );
96
+ }
97
+ if (results.failed > 0) {
98
+ const errorMessage =
99
+ results.errors.length > 3
100
+ ? `${results.errors.slice(0, 3).join(', ')}... and ${results.errors.length - 3} more`
101
+ : results.errors.join(', ');
102
+ toast.error(
103
+ `Failed to duplicate ${results.failed} ${entityName.toLowerCase()}s: ${errorMessage}`,
104
+ );
105
+ }
106
+
107
+ if (results.success > 0) {
108
+ refetchPaginatedList();
109
+ table.resetRowSelection();
110
+ onSuccess?.();
111
+ }
112
+ } finally {
113
+ setIsDuplicating(false);
114
+ setProgress({ completed: 0, total: 0 });
115
+ }
116
+ };
117
+
118
+ return (
119
+ <DataTableBulkActionItem
120
+ requiresPermission={requiredPermissions}
121
+ onClick={handleDuplicate}
122
+ label={
123
+ isDuplicating ? (
124
+ <Trans>
125
+ Duplicating... ({progress.completed}/{progress.total})
126
+ </Trans>
127
+ ) : (
128
+ <Trans>Duplicate</Trans>
129
+ )
130
+ }
131
+ icon={CopyIcon}
132
+ />
133
+ );
134
+ }
@@ -147,3 +147,12 @@ export const removeCollectionFromChannelDocument = graphql(`
147
147
  }
148
148
  }
149
149
  `);
150
+
151
+ export const deleteCollectionsDocument = graphql(`
152
+ mutation DeleteCollections($ids: [ID!]!) {
153
+ deleteCollections(ids: $ids) {
154
+ result
155
+ message
156
+ }
157
+ }
158
+ `);
@@ -16,6 +16,8 @@ import { useState } from 'react';
16
16
  import { collectionListDocument, deleteCollectionDocument } from './collections.graphql.js';
17
17
  import {
18
18
  AssignCollectionsToChannelBulkAction,
19
+ DeleteCollectionsBulkAction,
20
+ DuplicateCollectionsBulkAction,
19
21
  RemoveCollectionsFromChannelBulkAction,
20
22
  } from './components/collection-bulk-actions.js';
21
23
  import { CollectionContentsSheet } from './components/collection-contents-sheet.js';
@@ -192,6 +194,14 @@ function CollectionListPage() {
192
194
  component: RemoveCollectionsFromChannelBulkAction,
193
195
  order: 200,
194
196
  },
197
+ {
198
+ component: DuplicateCollectionsBulkAction,
199
+ order: 300,
200
+ },
201
+ {
202
+ component: DeleteCollectionsBulkAction,
203
+ order: 400,
204
+ },
195
205
  ]}
196
206
  >
197
207
  <PageActionBarRight>
@@ -1,17 +1,17 @@
1
1
  import { useMutation, useQueryClient } from '@tanstack/react-query';
2
- import { LayersIcon } from 'lucide-react';
2
+ import { LayersIcon, TrashIcon } from 'lucide-react';
3
3
  import { useState } from 'react';
4
4
  import { toast } from 'sonner';
5
5
 
6
6
  import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
7
7
  import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
8
8
  import { api } from '@/graphql/api.js';
9
- import { useChannel, usePaginatedList } from '@/index.js';
9
+ import { ResultOf, useChannel, usePaginatedList } from '@/index.js';
10
10
  import { Trans, useLingui } from '@/lib/trans.js';
11
-
12
- import { Permission } from '@vendure/common/lib/generated-types';
11
+ import { DuplicateBulkAction } from '../../../../common/duplicate-bulk-action.js';
13
12
  import {
14
13
  assignCollectionToChannelDocument,
14
+ deleteCollectionsDocument,
15
15
  removeCollectionFromChannelDocument,
16
16
  } from '../collections.graphql.js';
17
17
  import { AssignCollectionsToChannelDialog } from './assign-collections-to-channel-dialog.js';
@@ -35,7 +35,7 @@ export const AssignCollectionsToChannelBulkAction: BulkActionComponent<any> = ({
35
35
  return (
36
36
  <>
37
37
  <DataTableBulkActionItem
38
- requiresPermission={[Permission.UpdateCatalog, Permission.UpdateCollection]}
38
+ requiresPermission={['UpdateCatalog', 'UpdateCollection']}
39
39
  onClick={() => setDialogOpen(true)}
40
40
  label={<Trans>Assign to channel</Trans>}
41
41
  icon={LayersIcon}
@@ -84,7 +84,7 @@ export const RemoveCollectionsFromChannelBulkAction: BulkActionComponent<any> =
84
84
 
85
85
  return (
86
86
  <DataTableBulkActionItem
87
- requiresPermission={[Permission.UpdateCatalog, Permission.UpdateCollection]}
87
+ requiresPermission={['UpdateCatalog', 'UpdateCollection']}
88
88
  onClick={handleRemove}
89
89
  label={<Trans>Remove from current channel</Trans>}
90
90
  confirmationText={
@@ -97,3 +97,63 @@ export const RemoveCollectionsFromChannelBulkAction: BulkActionComponent<any> =
97
97
  />
98
98
  );
99
99
  };
100
+
101
+ export const DuplicateCollectionsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
102
+ const queryClient = useQueryClient();
103
+ return (
104
+ <DuplicateBulkAction
105
+ entityType="Collection"
106
+ duplicatorCode="collection-duplicator"
107
+ duplicatorArguments={[]}
108
+ requiredPermissions={['UpdateCatalog', 'UpdateCollection']}
109
+ entityName="Collection"
110
+ selection={selection}
111
+ table={table}
112
+ onSuccess={() => {
113
+ queryClient.invalidateQueries({ queryKey: ['childCollections'] });
114
+ }}
115
+ />
116
+ );
117
+ };
118
+
119
+ export const DeleteCollectionsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
120
+ const { refetchPaginatedList } = usePaginatedList();
121
+ const { i18n } = useLingui();
122
+ const queryClient = useQueryClient();
123
+ const { mutate } = useMutation({
124
+ mutationFn: api.mutate(deleteCollectionsDocument),
125
+ onSuccess: (result: ResultOf<typeof deleteCollectionsDocument>) => {
126
+ let deleted = 0;
127
+ const errors: string[] = [];
128
+ for (const item of result.deleteCollections) {
129
+ if (item.result === 'DELETED') {
130
+ deleted++;
131
+ } else if (item.message) {
132
+ errors.push(item.message);
133
+ }
134
+ }
135
+ if (0 < deleted) {
136
+ toast.success(i18n.t(`Deleted ${deleted} collections`));
137
+ }
138
+ if (0 < errors.length) {
139
+ toast.error(i18n.t(`Failed to delete ${errors.length} collections`));
140
+ }
141
+ refetchPaginatedList();
142
+ table.resetRowSelection();
143
+ queryClient.invalidateQueries({ queryKey: ['childCollections'] });
144
+ },
145
+ onError: () => {
146
+ toast.error(`Failed to delete ${selection.length} collections`);
147
+ },
148
+ });
149
+ return (
150
+ <DataTableBulkActionItem
151
+ requiresPermission={['DeleteCatalog', 'DeleteCollection']}
152
+ onClick={() => mutate({ ids: selection.map(s => s.id) })}
153
+ label={<Trans>Delete</Trans>}
154
+ confirmationText={<Trans>Are you sure you want to delete {selection.length} collections?</Trans>}
155
+ icon={TrashIcon}
156
+ className="text-destructive"
157
+ />
158
+ );
159
+ };
@@ -10,7 +10,6 @@ import { ResultOf } from '@/graphql/graphql.js';
10
10
  import { useChannel, usePaginatedList } from '@/index.js';
11
11
  import { Trans, useLingui } from '@/lib/trans.js';
12
12
 
13
- import { Permission } from '@vendure/common/lib/generated-types';
14
13
  import { AssignFacetValuesDialog } from '../../_products/components/assign-facet-values-dialog.js';
15
14
  import { AssignToChannelDialog } from '../../_products/components/assign-to-channel-dialog.js';
16
15
  import {
@@ -52,7 +51,7 @@ export const DeleteProductVariantsBulkAction: BulkActionComponent<any> = ({ sele
52
51
  });
53
52
  return (
54
53
  <DataTableBulkActionItem
55
- requiresPermission={[Permission.DeleteCatalog, Permission.DeleteProduct]}
54
+ requiresPermission={['DeleteCatalog', 'DeleteProduct']}
56
55
  onClick={() => mutate({ ids: selection.map(s => s.id) })}
57
56
  label={<Trans>Delete</Trans>}
58
57
  confirmationText={
@@ -81,7 +80,7 @@ export const AssignProductVariantsToChannelBulkAction: BulkActionComponent<any>
81
80
  return (
82
81
  <>
83
82
  <DataTableBulkActionItem
84
- requiresPermission={[Permission.UpdateCatalog, Permission.UpdateProduct]}
83
+ requiresPermission={['UpdateCatalog', 'UpdateProduct']}
85
84
  onClick={() => setDialogOpen(true)}
86
85
  label={<Trans>Assign to channel</Trans>}
87
86
  icon={LayersIcon}
@@ -134,7 +133,7 @@ export const RemoveProductVariantsFromChannelBulkAction: BulkActionComponent<any
134
133
 
135
134
  return (
136
135
  <DataTableBulkActionItem
137
- requiresPermission={[Permission.UpdateCatalog, Permission.UpdateProduct]}
136
+ requiresPermission={['UpdateCatalog', 'UpdateProduct']}
138
137
  onClick={handleRemove}
139
138
  label={<Trans>Remove from current channel</Trans>}
140
139
  confirmationText={
@@ -164,7 +163,7 @@ export const AssignFacetValuesToProductVariantsBulkAction: BulkActionComponent<a
164
163
  return (
165
164
  <>
166
165
  <DataTableBulkActionItem
167
- requiresPermission={[Permission.UpdateCatalog, Permission.UpdateProduct]}
166
+ requiresPermission={['UpdateCatalog', 'UpdateProduct']}
168
167
  onClick={() => setDialogOpen(true)}
169
168
  label={<Trans>Edit facet values</Trans>}
170
169
  icon={TagIcon}