@vendure/dashboard 3.3.6-master-202507010922 → 3.3.6-master-202507020234

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 (78) hide show
  1. package/package.json +131 -131
  2. package/src/app/common/delete-bulk-action.tsx +147 -0
  3. package/src/app/common/duplicate-bulk-action.tsx +1 -1
  4. package/src/app/routes/_authenticated/_administrators/administrators.graphql.ts +9 -0
  5. package/src/app/routes/_authenticated/_administrators/administrators.tsx +7 -0
  6. package/src/app/routes/_authenticated/_administrators/components/administrator-bulk-actions.tsx +15 -0
  7. package/src/app/routes/_authenticated/_assets/assets.graphql.ts +11 -0
  8. package/src/app/routes/_authenticated/_assets/assets.tsx +10 -2
  9. package/src/app/routes/_authenticated/_assets/components/asset-bulk-actions.tsx +45 -0
  10. package/src/app/routes/_authenticated/_channels/channels.graphql.ts +9 -0
  11. package/src/app/routes/_authenticated/_channels/channels.tsx +7 -0
  12. package/src/app/routes/_authenticated/_channels/components/channel-bulk-actions.tsx +15 -0
  13. package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +39 -110
  14. package/src/app/routes/_authenticated/_countries/components/country-bulk-actions.tsx +15 -0
  15. package/src/app/routes/_authenticated/_countries/countries.graphql.ts +9 -0
  16. package/src/app/routes/_authenticated/_countries/countries.tsx +7 -0
  17. package/src/app/routes/_authenticated/_customer-groups/components/customer-group-bulk-actions.tsx +15 -0
  18. package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +9 -0
  19. package/src/app/routes/_authenticated/_customer-groups/customer-groups.tsx +7 -0
  20. package/src/app/routes/_authenticated/_customers/components/customer-bulk-actions.tsx +15 -0
  21. package/src/app/routes/_authenticated/_customers/customers.graphql.ts +9 -1
  22. package/src/app/routes/_authenticated/_customers/customers.tsx +7 -0
  23. package/src/app/routes/_authenticated/_facets/components/facet-bulk-actions.tsx +104 -0
  24. package/src/app/routes/_authenticated/_facets/facets.graphql.ts +30 -0
  25. package/src/app/routes/_authenticated/_facets/facets.tsx +24 -0
  26. package/src/app/routes/_authenticated/_payment-methods/components/payment-method-bulk-actions.tsx +58 -0
  27. package/src/app/routes/_authenticated/_payment-methods/payment-methods.graphql.ts +27 -0
  28. package/src/app/routes/_authenticated/_payment-methods/payment-methods.tsx +30 -8
  29. package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +4 -1
  30. package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +36 -110
  31. package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +36 -105
  32. package/src/app/routes/_authenticated/_promotions/components/promotion-bulk-actions.tsx +82 -0
  33. package/src/app/routes/_authenticated/_promotions/promotions.graphql.ts +25 -0
  34. package/src/app/routes/_authenticated/_promotions/promotions.tsx +24 -0
  35. package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +1 -1
  36. package/src/app/routes/_authenticated/_roles/components/role-bulk-actions.tsx +15 -0
  37. package/src/app/routes/_authenticated/_roles/roles.graphql.ts +9 -0
  38. package/src/app/routes/_authenticated/_roles/roles.tsx +7 -0
  39. package/src/app/routes/_authenticated/_sellers/components/seller-bulk-actions.tsx +15 -0
  40. package/src/app/routes/_authenticated/_sellers/sellers.graphql.ts +9 -0
  41. package/src/app/routes/_authenticated/_sellers/sellers.tsx +7 -0
  42. package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +1 -1
  43. package/src/app/routes/_authenticated/_shipping-methods/components/shipping-method-bulk-actions.tsx +61 -0
  44. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.graphql.ts +27 -0
  45. package/src/app/routes/_authenticated/_shipping-methods/shipping-methods.tsx +19 -0
  46. package/src/app/routes/_authenticated/_stock-locations/components/stock-location-bulk-actions.tsx +58 -0
  47. package/src/app/routes/_authenticated/_stock-locations/stock-locations.graphql.ts +25 -0
  48. package/src/app/routes/_authenticated/_stock-locations/stock-locations.tsx +19 -0
  49. package/src/app/routes/_authenticated/_tax-categories/components/tax-category-bulk-actions.tsx +15 -0
  50. package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +9 -0
  51. package/src/app/routes/_authenticated/_tax-categories/tax-categories.tsx +7 -0
  52. package/src/app/routes/_authenticated/_tax-rates/components/tax-rate-bulk-actions.tsx +15 -0
  53. package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +9 -0
  54. package/src/app/routes/_authenticated/_tax-rates/tax-rates.tsx +7 -0
  55. package/src/app/routes/_authenticated/_zones/components/zone-bulk-actions.tsx +15 -0
  56. package/src/app/routes/_authenticated/_zones/zones.graphql.ts +9 -0
  57. package/src/app/routes/_authenticated/_zones/zones.tsx +7 -0
  58. package/src/lib/components/shared/asset/asset-bulk-actions.tsx +90 -0
  59. package/src/lib/components/shared/asset/asset-gallery.tsx +12 -7
  60. package/src/lib/components/shared/assign-to-channel-bulk-action.tsx +70 -0
  61. package/src/{app/routes/_authenticated/_products/components → lib/components/shared}/assign-to-channel-dialog.tsx +48 -30
  62. package/src/lib/components/shared/detail-page-button.tsx +1 -1
  63. package/src/lib/components/shared/remove-from-channel-bulk-action.tsx +89 -0
  64. package/src/lib/framework/extension-api/use-dashboard-extensions.ts +2 -1
  65. package/src/lib/framework/form-engine/use-generated-form.tsx +5 -8
  66. package/src/lib/framework/page/use-detail-page.ts +4 -4
  67. package/src/lib/framework/page/use-extended-router.tsx +4 -5
  68. package/src/lib/hooks/use-channel.ts +2 -1
  69. package/src/lib/hooks/use-extended-detail-query.ts +2 -1
  70. package/src/lib/hooks/use-extended-list-query.ts +3 -2
  71. package/src/lib/hooks/use-grouped-permissions.ts +4 -2
  72. package/src/lib/hooks/use-page-block.tsx +1 -1
  73. package/src/lib/hooks/use-page.tsx +2 -2
  74. package/src/lib/hooks/use-permissions.ts +3 -2
  75. package/src/lib/hooks/use-server-config.ts +2 -1
  76. package/src/lib/hooks/use-theme.ts +2 -1
  77. package/src/lib/providers/auth.tsx +34 -11
  78. package/src/app/routes/_authenticated/_collections/components/assign-collections-to-channel-dialog.tsx +0 -110
package/package.json CHANGED
@@ -1,134 +1,134 @@
1
1
  {
2
- "name": "@vendure/dashboard",
3
- "private": false,
4
- "version": "3.3.6-master-202507010922",
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-202507020234",
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-202507010922",
90
- "@vendure/core": "^3.3.6-master-202507010922",
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": "ea754102804748c447cf9c1d28d9bbc5cc0a333b"
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-202507020234",
90
+ "@vendure/core": "^3.3.6-master-202507020234",
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": "1ebfbfe0c8cc2a8f545987f52fd06b7f9a1f3089"
134
134
  }
@@ -0,0 +1,147 @@
1
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
2
+ import { TrashIcon } from 'lucide-react';
3
+ import { toast } from 'sonner';
4
+
5
+ import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
6
+ import { api } from '@/graphql/api.js';
7
+ import { getMutationName, usePaginatedList } from '@/index.js';
8
+ import { Trans, useLingui } from '@/lib/trans.js';
9
+
10
+ interface DeleteBulkActionProps {
11
+ /** The GraphQL mutation document to execute */
12
+ mutationDocument: any;
13
+ /** The entity name for localization (e.g., "products", "administrators") */
14
+ entityName: string;
15
+ /** The required permissions for this action */
16
+ requiredPermissions: string[];
17
+ /** Optional callback for additional cleanup after successful deletion */
18
+ onSuccess?: () => void;
19
+ /** Optional query keys to invalidate after successful deletion */
20
+ invalidateQueries?: string[];
21
+ /** The selected items to delete */
22
+ selection: any[];
23
+ /** The table instance */
24
+ table: any;
25
+ }
26
+
27
+ /**
28
+ * A reusable delete bulk action component that can be used across all bulk action files.
29
+ *
30
+ * This component handles the common pattern of deleting multiple entities:
31
+ * - Executes the provided GraphQL mutation
32
+ * - Shows success/error toasts with localized messages
33
+ * - Refetches the paginated list
34
+ * - Resets table selection
35
+ * - Optionally invalidates additional queries
36
+ * - Optionally calls additional success callbacks
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * // Basic usage
41
+ * export const DeleteProductsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
42
+ * return (
43
+ * <DeleteBulkAction
44
+ * mutationDocument={deleteProductsDocument}
45
+ * entityName="products"
46
+ * requiredPermissions={['DeleteCatalog', 'DeleteProduct']}
47
+ * selection={selection}
48
+ * table={table}
49
+ * />
50
+ * );
51
+ * };
52
+ *
53
+ * // With additional cleanup
54
+ * export const DeleteCollectionsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
55
+ * return (
56
+ * <DeleteBulkAction
57
+ * mutationDocument={deleteCollectionsDocument}
58
+ * entityName="collections"
59
+ * requiredPermissions={['DeleteCatalog', 'DeleteCollection']}
60
+ * invalidateQueries={['childCollections']}
61
+ * onSuccess={() => {
62
+ * // Additional cleanup logic
63
+ * }}
64
+ * selection={selection}
65
+ * table={table}
66
+ * />
67
+ * );
68
+ * };
69
+ * ```
70
+ */
71
+ export function DeleteBulkAction({
72
+ mutationDocument,
73
+ entityName,
74
+ requiredPermissions,
75
+ onSuccess,
76
+ invalidateQueries = [],
77
+ selection,
78
+ table,
79
+ }: Readonly<DeleteBulkActionProps>) {
80
+ const { refetchPaginatedList } = usePaginatedList();
81
+ const { i18n } = useLingui();
82
+ const queryClient = useQueryClient();
83
+
84
+ const { mutate } = useMutation({
85
+ mutationFn: api.mutate(mutationDocument),
86
+ onSuccess: (result: any) => {
87
+ let deleted = 0;
88
+ let failed = 0;
89
+ const errors: string[] = [];
90
+
91
+ // Get the mutation result field name from the document
92
+ const mutationResultFieldName = getMutationName(mutationDocument);
93
+ const deleteResults = result[mutationResultFieldName];
94
+
95
+ for (const item of deleteResults) {
96
+ if (item.result === 'DELETED') {
97
+ deleted++;
98
+ } else {
99
+ failed++;
100
+ if (item.message) {
101
+ errors.push(item.message);
102
+ }
103
+ }
104
+ }
105
+
106
+ if (0 < deleted) {
107
+ toast.success(i18n.t(`Deleted ${deleted} ${entityName}`));
108
+ }
109
+ if (0 < failed) {
110
+ const errorMessage =
111
+ errors.length > 0
112
+ ? i18n.t(`Failed to delete ${failed} ${entityName}: ${errors.join(', ')}`)
113
+ : i18n.t(`Failed to delete ${failed} ${entityName}`);
114
+ toast.error(errorMessage);
115
+ }
116
+
117
+ refetchPaginatedList();
118
+ table.resetRowSelection();
119
+
120
+ // Invalidate additional queries if specified
121
+ invalidateQueries.forEach(queryKey => {
122
+ queryClient.invalidateQueries({ queryKey: [queryKey] });
123
+ });
124
+
125
+ // Call additional success callback if provided
126
+ onSuccess?.();
127
+ },
128
+ onError: () => {
129
+ toast.error(`Failed to delete ${selection.length} ${entityName}`);
130
+ },
131
+ });
132
+
133
+ return (
134
+ <DataTableBulkActionItem
135
+ requiresPermission={requiredPermissions}
136
+ onClick={() => mutate({ ids: selection.map(s => s.id) })}
137
+ label={<Trans>Delete</Trans>}
138
+ confirmationText={
139
+ <Trans>
140
+ Are you sure you want to delete {selection.length} {entityName}?
141
+ </Trans>
142
+ }
143
+ icon={TrashIcon}
144
+ className="text-destructive"
145
+ />
146
+ );
147
+ }
@@ -10,7 +10,7 @@ import { usePaginatedList } from '@/index.js';
10
10
  import { Trans, useLingui } from '@/lib/trans.js';
11
11
 
12
12
  interface DuplicateBulkActionProps {
13
- entityType: 'Product' | 'Collection';
13
+ entityType: 'Product' | 'Collection' | 'Facet' | 'Promotion';
14
14
  duplicatorCode: string;
15
15
  duplicatorArguments?: Array<{ name: string; value: string }>;
16
16
  requiredPermissions: string[];
@@ -77,3 +77,12 @@ export const deleteAdministratorDocument = graphql(`
77
77
  }
78
78
  }
79
79
  `);
80
+
81
+ export const deleteAdministratorsDocument = graphql(`
82
+ mutation DeleteAdministrators($ids: [ID!]!) {
83
+ deleteAdministrators(ids: $ids) {
84
+ result
85
+ message
86
+ }
87
+ }
88
+ `);
@@ -9,6 +9,7 @@ import { Trans } from '@/lib/trans.js';
9
9
  import { createFileRoute, Link } from '@tanstack/react-router';
10
10
  import { PlusIcon } from 'lucide-react';
11
11
  import { administratorListDocument, deleteAdministratorDocument } from './administrators.graphql.js';
12
+ import { DeleteAdministratorsBulkAction } from './components/administrator-bulk-actions.js';
12
13
 
13
14
  export const Route = createFileRoute('/_authenticated/_administrators/administrators')({
14
15
  component: AdministratorListPage,
@@ -70,6 +71,12 @@ function AdministratorListPage() {
70
71
  emailAddress: true,
71
72
  }}
72
73
  defaultColumnOrder={['name', 'emailAddress', 'roles']}
74
+ bulkActions={[
75
+ {
76
+ component: DeleteAdministratorsBulkAction,
77
+ order: 500,
78
+ },
79
+ ]}
73
80
  >
74
81
  <PageActionBarRight>
75
82
  <PermissionGuard requires={['CreateAdministrator']}>
@@ -0,0 +1,15 @@
1
+ import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
2
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
3
+ import { deleteAdministratorsDocument } from '../administrators.graphql.js';
4
+
5
+ export const DeleteAdministratorsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
6
+ return (
7
+ <DeleteBulkAction
8
+ mutationDocument={deleteAdministratorsDocument}
9
+ entityName="administrators"
10
+ requiredPermissions={['DeleteAdministrator']}
11
+ selection={selection}
12
+ table={table}
13
+ />
14
+ );
15
+ };
@@ -24,3 +24,14 @@ export const assetUpdateDocument = graphql(`
24
24
  }
25
25
  }
26
26
  `);
27
+
28
+ export const deleteAssetsDocument = graphql(`
29
+ mutation DeleteAssets($input: DeleteAssetsInput!) {
30
+ deleteAssets(input: $input) {
31
+ ... on DeletionResponse {
32
+ result
33
+ message
34
+ }
35
+ }
36
+ }
37
+ `);
@@ -1,7 +1,8 @@
1
1
  import { AssetGallery } from '@/components/shared/asset/asset-gallery.js';
2
- import { Page, PageTitle, PageActionBar } from '@/framework/layout-engine/page-layout.js';
2
+ import { Page, PageBlock, PageTitle } from '@/framework/layout-engine/page-layout.js';
3
3
  import { Trans } from '@/lib/trans.js';
4
4
  import { createFileRoute } from '@tanstack/react-router';
5
+ import { DeleteAssetsBulkAction } from './components/asset-bulk-actions.js';
5
6
 
6
7
  export const Route = createFileRoute('/_authenticated/_assets/assets')({
7
8
  component: RouteComponent,
@@ -13,7 +14,14 @@ function RouteComponent() {
13
14
  <PageTitle>
14
15
  <Trans>Assets</Trans>
15
16
  </PageTitle>
16
- <AssetGallery selectable={true} multiSelect='manual' />
17
+ <PageBlock blockId="asset-gallery" column="main">
18
+ <AssetGallery selectable={true} multiSelect="manual" bulkActions={[{
19
+ order: 10,
20
+ component: DeleteAssetsBulkAction,
21
+ },
22
+ ]}
23
+ />
24
+ </PageBlock>
17
25
  </Page>
18
26
  );
19
27
  }
@@ -0,0 +1,45 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import { TrashIcon } from 'lucide-react';
3
+ import { toast } from 'sonner';
4
+
5
+ import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
6
+ import { api } from '@/graphql/api.js';
7
+ import { AssetFragment } from '@/graphql/fragments.js';
8
+ import { ResultOf } from '@/graphql/graphql.js';
9
+ import { Trans, useLingui } from '@/lib/trans.js';
10
+ import { deleteAssetsDocument } from '../assets.graphql.js';
11
+
12
+ export const DeleteAssetsBulkAction = ({
13
+ selection,
14
+ refetch,
15
+ }: {
16
+ selection: AssetFragment[];
17
+ refetch: () => void;
18
+ }) => {
19
+ const { i18n } = useLingui();
20
+ const { mutate } = useMutation({
21
+ mutationFn: api.mutate(deleteAssetsDocument),
22
+ onSuccess: (result: ResultOf<typeof deleteAssetsDocument>) => {
23
+ if (result.deleteAssets.result === 'DELETED') {
24
+ toast.success(i18n.t(`Deleted ${selection.length} assets`));
25
+ } else {
26
+ toast.error(i18n.t(`Failed to delete assets: ${result.deleteAssets.message}`));
27
+ }
28
+ refetch();
29
+ },
30
+ onError: () => {
31
+ toast.error(`Failed to delete ${selection.length} assets`);
32
+ },
33
+ });
34
+
35
+ return (
36
+ <DataTableBulkActionItem
37
+ requiresPermission={['DeleteCatalog', 'DeleteAsset']}
38
+ onClick={() => mutate({ input: { assetIds: selection.map(s => s.id) } })}
39
+ label={<Trans>Delete</Trans>}
40
+ confirmationText={<Trans>Are you sure you want to delete {selection.length} assets?</Trans>}
41
+ icon={TrashIcon}
42
+ className="text-destructive"
43
+ />
44
+ );
45
+ };
@@ -91,3 +91,12 @@ export const deleteChannelDocument = graphql(`
91
91
  }
92
92
  }
93
93
  `);
94
+
95
+ export const deleteChannelsDocument = graphql(`
96
+ mutation DeleteChannels($ids: [ID!]!) {
97
+ deleteChannels(ids: $ids) {
98
+ result
99
+ message
100
+ }
101
+ }
102
+ `);
@@ -9,6 +9,7 @@ import { createFileRoute, Link } from '@tanstack/react-router';
9
9
  import { PlusIcon } from 'lucide-react';
10
10
  import { channelListQuery, deleteChannelDocument } from './channels.graphql.js';
11
11
  import { useLocalFormat } from '@/hooks/use-local-format.js';
12
+ import { DeleteChannelsBulkAction } from './components/channel-bulk-actions.js';
12
13
 
13
14
  export const Route = createFileRoute('/_authenticated/_channels/channels')({
14
15
  component: ChannelListPage,
@@ -62,6 +63,12 @@ function ChannelListPage() {
62
63
  }
63
64
  },
64
65
  }}
66
+ bulkActions={[
67
+ {
68
+ component: DeleteChannelsBulkAction,
69
+ order: 500,
70
+ },
71
+ ]}
65
72
  >
66
73
  <PageActionBarRight>
67
74
  <PermissionGuard requires={['CreateChannel']}>
@@ -0,0 +1,15 @@
1
+ import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
2
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
3
+ import { deleteChannelsDocument } from '../channels.graphql.js';
4
+
5
+ export const DeleteChannelsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
6
+ return (
7
+ <DeleteBulkAction
8
+ mutationDocument={deleteChannelsDocument}
9
+ entityName="channels"
10
+ requiredPermissions={['DeleteChannel']}
11
+ selection={selection}
12
+ table={table}
13
+ />
14
+ );
15
+ };