@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
@@ -1,99 +1,57 @@
1
- import { useMutation, useQueryClient } from '@tanstack/react-query';
2
- import { LayersIcon, TrashIcon } from 'lucide-react';
3
- import { useState } from 'react';
4
- import { toast } from 'sonner';
1
+ import { useQueryClient } from '@tanstack/react-query';
5
2
 
6
- import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
3
+ import { AssignToChannelBulkAction } from '@/components/shared/assign-to-channel-bulk-action.js';
4
+ import { RemoveFromChannelBulkAction } from '@/components/shared/remove-from-channel-bulk-action.js';
7
5
  import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
8
6
  import { api } from '@/graphql/api.js';
9
- import { ResultOf, useChannel, usePaginatedList } from '@/index.js';
10
- import { Trans, useLingui } from '@/lib/trans.js';
7
+ import { useChannel } from '@/index.js';
8
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
11
9
  import { DuplicateBulkAction } from '../../../../common/duplicate-bulk-action.js';
12
10
  import {
13
11
  assignCollectionToChannelDocument,
14
12
  deleteCollectionsDocument,
15
13
  removeCollectionFromChannelDocument,
16
14
  } from '../collections.graphql.js';
17
- import { AssignCollectionsToChannelDialog } from './assign-collections-to-channel-dialog.js';
18
15
 
19
16
  export const AssignCollectionsToChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
20
- const { refetchPaginatedList } = usePaginatedList();
21
- const { channels } = useChannel();
22
- const [dialogOpen, setDialogOpen] = useState(false);
23
17
  const queryClient = useQueryClient();
24
18
 
25
- if (channels.length < 2) {
26
- return null;
27
- }
28
-
29
- const handleSuccess = () => {
30
- refetchPaginatedList();
31
- table.resetRowSelection();
32
- queryClient.invalidateQueries({ queryKey: ['childCollections'] });
33
- };
34
-
35
19
  return (
36
- <>
37
- <DataTableBulkActionItem
38
- requiresPermission={['UpdateCatalog', 'UpdateCollection']}
39
- onClick={() => setDialogOpen(true)}
40
- label={<Trans>Assign to channel</Trans>}
41
- icon={LayersIcon}
42
- />
43
- <AssignCollectionsToChannelDialog
44
- open={dialogOpen}
45
- onOpenChange={setDialogOpen}
46
- entityIds={selection.map(s => s.id)}
47
- mutationFn={api.mutate(assignCollectionToChannelDocument)}
48
- onSuccess={handleSuccess}
49
- />
50
- </>
20
+ <AssignToChannelBulkAction
21
+ selection={selection}
22
+ table={table}
23
+ entityType="collections"
24
+ mutationFn={api.mutate(assignCollectionToChannelDocument)}
25
+ requiredPermissions={['UpdateCatalog', 'UpdateCollection']}
26
+ buildInput={(channelId: string) => ({
27
+ collectionIds: selection.map(s => s.id),
28
+ channelId,
29
+ })}
30
+ onSuccess={() => {
31
+ queryClient.invalidateQueries({ queryKey: ['childCollections'] });
32
+ }}
33
+ />
51
34
  );
52
35
  };
53
36
 
54
37
  export const RemoveCollectionsFromChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
55
- const { refetchPaginatedList } = usePaginatedList();
56
38
  const { selectedChannel } = useChannel();
57
- const { i18n } = useLingui();
58
39
  const queryClient = useQueryClient();
59
- const { mutate } = useMutation({
60
- mutationFn: api.mutate(removeCollectionFromChannelDocument),
61
- onSuccess: () => {
62
- toast.success(i18n.t(`Successfully removed ${selection.length} collections from channel`));
63
- refetchPaginatedList();
64
- table.resetRowSelection();
65
- queryClient.invalidateQueries({ queryKey: ['childCollections'] });
66
- },
67
- onError: error => {
68
- toast.error(`Failed to remove ${selection.length} collections from channel: ${error.message}`);
69
- },
70
- });
71
-
72
- if (!selectedChannel) {
73
- return null;
74
- }
75
-
76
- const handleRemove = () => {
77
- mutate({
78
- input: {
79
- collectionIds: selection.map(s => s.id),
80
- channelId: selectedChannel.id,
81
- },
82
- });
83
- };
84
40
 
85
41
  return (
86
- <DataTableBulkActionItem
87
- requiresPermission={['UpdateCatalog', 'UpdateCollection']}
88
- onClick={handleRemove}
89
- label={<Trans>Remove from current channel</Trans>}
90
- confirmationText={
91
- <Trans>
92
- Are you sure you want to remove {selection.length} collections from the current channel?
93
- </Trans>
94
- }
95
- icon={LayersIcon}
96
- className="text-warning"
42
+ <RemoveFromChannelBulkAction
43
+ selection={selection}
44
+ table={table}
45
+ entityType="collections"
46
+ mutationFn={api.mutate(removeCollectionFromChannelDocument)}
47
+ requiredPermissions={['UpdateCatalog', 'UpdateCollection']}
48
+ buildInput={() => ({
49
+ collectionIds: selection.map(s => s.id),
50
+ channelId: selectedChannel?.id,
51
+ })}
52
+ onSuccess={() => {
53
+ queryClient.invalidateQueries({ queryKey: ['childCollections'] });
54
+ }}
97
55
  />
98
56
  );
99
57
  };
@@ -117,43 +75,14 @@ export const DuplicateCollectionsBulkAction: BulkActionComponent<any> = ({ selec
117
75
  };
118
76
 
119
77
  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
78
  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"
79
+ <DeleteBulkAction
80
+ mutationDocument={deleteCollectionsDocument}
81
+ entityName="collections"
82
+ requiredPermissions={['DeleteCatalog', 'DeleteCollection']}
83
+ invalidateQueries={['childCollections']}
84
+ selection={selection}
85
+ table={table}
157
86
  />
158
87
  );
159
88
  };
@@ -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 { deleteCountriesDocument } from '../countries.graphql.js';
4
+
5
+ export const DeleteCountriesBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
6
+ return (
7
+ <DeleteBulkAction
8
+ mutationDocument={deleteCountriesDocument}
9
+ entityName="countries"
10
+ requiredPermissions={['DeleteCountry']}
11
+ selection={selection}
12
+ table={table}
13
+ />
14
+ );
15
+ };
@@ -67,3 +67,12 @@ export const deleteCountryDocument = graphql(`
67
67
  }
68
68
  }
69
69
  `);
70
+
71
+ export const deleteCountriesDocument = graphql(`
72
+ mutation DeleteCountries($ids: [ID!]!) {
73
+ deleteCountries(ids: $ids) {
74
+ result
75
+ message
76
+ }
77
+ }
78
+ `);
@@ -6,6 +6,7 @@ import { ListPage } from '@/framework/page/list-page.js';
6
6
  import { Trans } from '@/lib/trans.js';
7
7
  import { createFileRoute, Link } from '@tanstack/react-router';
8
8
  import { PlusIcon } from 'lucide-react';
9
+ import { DeleteCountriesBulkAction } from './components/country-bulk-actions.js';
9
10
  import { countriesListQuery, deleteCountryDocument } from './countries.graphql.js';
10
11
 
11
12
  export const Route = createFileRoute('/_authenticated/_countries/countries')({
@@ -51,6 +52,12 @@ function CountryListPage() {
51
52
  cell: ({ row }) => <DetailPageButton id={row.original.id} label={row.original.name} />,
52
53
  },
53
54
  }}
55
+ bulkActions={[
56
+ {
57
+ component: DeleteCountriesBulkAction,
58
+ order: 500,
59
+ },
60
+ ]}
54
61
  >
55
62
  <PageActionBarRight>
56
63
  <PermissionGuard requires={['CreateCountry']}>
@@ -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 { deleteCustomerGroupsDocument } from '../customer-groups.graphql.js';
4
+
5
+ export const DeleteCustomerGroupsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
6
+ return (
7
+ <DeleteBulkAction
8
+ mutationDocument={deleteCustomerGroupsDocument}
9
+ entityName="customer groups"
10
+ requiredPermissions={['DeleteCustomerGroup']}
11
+ selection={selection}
12
+ table={table}
13
+ />
14
+ );
15
+ };
@@ -69,3 +69,12 @@ export const deleteCustomerGroupDocument = graphql(`
69
69
  }
70
70
  }
71
71
  `);
72
+
73
+ export const deleteCustomerGroupsDocument = graphql(`
74
+ mutation DeleteCustomerGroups($ids: [ID!]!) {
75
+ deleteCustomerGroups(ids: $ids) {
76
+ result
77
+ message
78
+ }
79
+ }
80
+ `);
@@ -6,6 +6,7 @@ import { ListPage } from '@/framework/page/list-page.js';
6
6
  import { Trans } from '@/lib/trans.js';
7
7
  import { createFileRoute, Link } from '@tanstack/react-router';
8
8
  import { PlusIcon } from 'lucide-react';
9
+ import { DeleteCustomerGroupsBulkAction } from './components/customer-group-bulk-actions.js';
9
10
  import { CustomerGroupMembersSheet } from './components/customer-group-members-sheet.js';
10
11
  import { customerGroupListDocument, deleteCustomerGroupDocument } from './customer-groups.graphql.js';
11
12
 
@@ -52,6 +53,12 @@ function CustomerGroupListPage() {
52
53
  name: { contains: searchTerm },
53
54
  };
54
55
  }}
56
+ bulkActions={[
57
+ {
58
+ component: DeleteCustomerGroupsBulkAction,
59
+ order: 500,
60
+ },
61
+ ]}
55
62
  >
56
63
  <PageActionBarRight>
57
64
  <PermissionGuard requires={['CreateCustomerGroup']}>
@@ -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 { deleteCustomersDocument } from '../customers.graphql.js';
4
+
5
+ export const DeleteCustomersBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
6
+ return (
7
+ <DeleteBulkAction
8
+ mutationDocument={deleteCustomersDocument}
9
+ entityName="customers"
10
+ requiredPermissions={['DeleteCustomer']}
11
+ selection={selection}
12
+ table={table}
13
+ />
14
+ );
15
+ };
@@ -1,5 +1,4 @@
1
1
  import { graphql } from '@/graphql/graphql.js';
2
- import { gql } from 'awesome-graphql-client';
3
2
 
4
3
  export const customerListDocument = graphql(`
5
4
  query GetCustomerList($options: CustomerListOptions) {
@@ -202,3 +201,12 @@ export const removeCustomerFromGroupDocument = graphql(`
202
201
  }
203
202
  }
204
203
  `);
204
+
205
+ export const deleteCustomersDocument = graphql(`
206
+ mutation DeleteCustomers($ids: [ID!]!) {
207
+ deleteCustomers(ids: $ids) {
208
+ result
209
+ message
210
+ }
211
+ }
212
+ `);
@@ -6,6 +6,7 @@ import { ListPage } from '@/framework/page/list-page.js';
6
6
  import { Trans } from '@/lib/trans.js';
7
7
  import { createFileRoute, Link } from '@tanstack/react-router';
8
8
  import { PlusIcon } from 'lucide-react';
9
+ import { DeleteCustomersBulkAction } from './components/customer-bulk-actions.js';
9
10
  import { CustomerStatusBadge } from './components/customer-status-badge.js';
10
11
  import { customerListDocument, deleteCustomerDocument } from './customers.graphql.js';
11
12
 
@@ -66,6 +67,12 @@ function CustomerListPage() {
66
67
  firstName: false,
67
68
  lastName: false,
68
69
  }}
70
+ bulkActions={[
71
+ {
72
+ component: DeleteCustomersBulkAction,
73
+ order: 500,
74
+ },
75
+ ]}
69
76
  >
70
77
  <PageActionBarRight>
71
78
  <PermissionGuard requires={['CreateCustomer']}>
@@ -0,0 +1,104 @@
1
+ import { toast } from 'sonner';
2
+
3
+ import { AssignToChannelBulkAction } from '@/components/shared/assign-to-channel-bulk-action.js';
4
+ import { RemoveFromChannelBulkAction } from '@/components/shared/remove-from-channel-bulk-action.js';
5
+ import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
6
+ import { api } from '@/graphql/api.js';
7
+ import { ResultOf } from '@/graphql/graphql.js';
8
+ import { useChannel } from '@/index.js';
9
+ import { useLingui } from '@/lib/trans.js';
10
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
11
+ import { DuplicateBulkAction } from '../../../../common/duplicate-bulk-action.js';
12
+
13
+ import {
14
+ assignFacetsToChannelDocument,
15
+ deleteFacetsDocument,
16
+ removeFacetsFromChannelDocument,
17
+ } from '../facets.graphql.js';
18
+
19
+ export const DeleteFacetsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
20
+ return (
21
+ <DeleteBulkAction
22
+ mutationDocument={deleteFacetsDocument}
23
+ entityName="facets"
24
+ requiredPermissions={['DeleteCatalog', 'DeleteFacet']}
25
+ selection={selection}
26
+ table={table}
27
+ />
28
+ );
29
+ };
30
+
31
+ export const AssignFacetsToChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
32
+ return (
33
+ <AssignToChannelBulkAction
34
+ selection={selection}
35
+ table={table}
36
+ entityType="facets"
37
+ mutationFn={api.mutate(assignFacetsToChannelDocument)}
38
+ requiredPermissions={['UpdateCatalog', 'UpdateFacet']}
39
+ buildInput={(channelId: string) => ({
40
+ facetIds: selection.map(s => s.id),
41
+ channelId,
42
+ })}
43
+ />
44
+ );
45
+ };
46
+
47
+ export const RemoveFacetsFromChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
48
+ const { selectedChannel } = useChannel();
49
+ const { i18n } = useLingui();
50
+
51
+ return (
52
+ <RemoveFromChannelBulkAction
53
+ selection={selection}
54
+ table={table}
55
+ entityType="facets"
56
+ mutationFn={api.mutate(removeFacetsFromChannelDocument)}
57
+ requiredPermissions={['UpdateCatalog', 'UpdateFacet']}
58
+ buildInput={() => ({
59
+ facetIds: selection.map(s => s.id),
60
+ channelId: selectedChannel?.id,
61
+ })}
62
+ onSuccess={result => {
63
+ const typedResult = result as ResultOf<typeof removeFacetsFromChannelDocument>;
64
+ if (typedResult?.removeFacetsFromChannel) {
65
+ const errors: string[] = [];
66
+
67
+ for (const item of typedResult.removeFacetsFromChannel) {
68
+ if ('id' in item) {
69
+ // Do nothing
70
+ } else if ('message' in item) {
71
+ errors.push(item.message);
72
+ toast.error(i18n.t(`Failed to remove facet from channel: ${item.message}`));
73
+ }
74
+ }
75
+
76
+ const successCount = selection.length - errors.length;
77
+
78
+ if (successCount > 0) {
79
+ toast.success(i18n.t(`Successfully removed ${successCount} facets from channel`));
80
+ }
81
+ }
82
+ }}
83
+ />
84
+ );
85
+ };
86
+
87
+ export const DuplicateFacetsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
88
+ return (
89
+ <DuplicateBulkAction
90
+ entityType="Facet"
91
+ duplicatorCode="facet-duplicator"
92
+ duplicatorArguments={[
93
+ {
94
+ name: 'includeValues',
95
+ value: 'true',
96
+ },
97
+ ]}
98
+ requiredPermissions={['UpdateCatalog', 'UpdateFacet']}
99
+ entityName="Facet"
100
+ selection={selection}
101
+ table={table}
102
+ />
103
+ );
104
+ };
@@ -102,3 +102,33 @@ export const deleteFacetDocument = graphql(`
102
102
  }
103
103
  }
104
104
  `);
105
+
106
+ export const assignFacetsToChannelDocument = graphql(`
107
+ mutation AssignFacetsToChannel($input: AssignFacetsToChannelInput!) {
108
+ assignFacetsToChannel(input: $input) {
109
+ id
110
+ }
111
+ }
112
+ `);
113
+
114
+ export const removeFacetsFromChannelDocument = graphql(`
115
+ mutation RemoveFacetsFromChannel($input: RemoveFacetsFromChannelInput!) {
116
+ removeFacetsFromChannel(input: $input) {
117
+ ... on Facet {
118
+ id
119
+ }
120
+ ... on ErrorResult {
121
+ message
122
+ }
123
+ }
124
+ }
125
+ `);
126
+
127
+ export const deleteFacetsDocument = graphql(`
128
+ mutation DeleteFacets($ids: [ID!]!) {
129
+ deleteFacets(ids: $ids) {
130
+ result
131
+ message
132
+ }
133
+ }
134
+ `);
@@ -8,6 +8,12 @@ import { Trans } from '@/lib/trans.js';
8
8
  import { createFileRoute, Link } from '@tanstack/react-router';
9
9
  import { ResultOf } from 'gql.tada';
10
10
  import { PlusIcon } from 'lucide-react';
11
+ import {
12
+ AssignFacetsToChannelBulkAction,
13
+ DeleteFacetsBulkAction,
14
+ DuplicateFacetsBulkAction,
15
+ RemoveFacetsFromChannelBulkAction,
16
+ } from './components/facet-bulk-actions.js';
11
17
  import { FacetValuesSheet } from './components/facet-values-sheet.js';
12
18
  import { deleteFacetDocument, facetListDocument } from './facets.graphql.js';
13
19
 
@@ -80,6 +86,24 @@ function FacetListPage() {
80
86
  },
81
87
  };
82
88
  }}
89
+ bulkActions={[
90
+ {
91
+ order: 100,
92
+ component: AssignFacetsToChannelBulkAction,
93
+ },
94
+ {
95
+ order: 200,
96
+ component: RemoveFacetsFromChannelBulkAction,
97
+ },
98
+ {
99
+ order: 300,
100
+ component: DuplicateFacetsBulkAction,
101
+ },
102
+ {
103
+ order: 400,
104
+ component: DeleteFacetsBulkAction,
105
+ },
106
+ ]}
83
107
  route={Route}
84
108
  >
85
109
  <PageActionBarRight>
@@ -0,0 +1,58 @@
1
+ import { AssignToChannelBulkAction } from '@/components/shared/assign-to-channel-bulk-action.js';
2
+ import { RemoveFromChannelBulkAction } from '@/components/shared/remove-from-channel-bulk-action.js';
3
+ import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
4
+ import { api } from '@/graphql/api.js';
5
+ import { useChannel } from '@/index.js';
6
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
7
+
8
+ import {
9
+ assignPaymentMethodsToChannelDocument,
10
+ deletePaymentMethodsDocument,
11
+ removePaymentMethodsFromChannelDocument,
12
+ } from '../payment-methods.graphql.js';
13
+
14
+ export const DeletePaymentMethodsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
15
+ return (
16
+ <DeleteBulkAction
17
+ mutationDocument={deletePaymentMethodsDocument}
18
+ entityName="payment methods"
19
+ requiredPermissions={['DeletePaymentMethod']}
20
+ selection={selection}
21
+ table={table}
22
+ />
23
+ );
24
+ };
25
+
26
+ export const AssignPaymentMethodsToChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
27
+ return (
28
+ <AssignToChannelBulkAction
29
+ selection={selection}
30
+ table={table}
31
+ entityType="payment methods"
32
+ mutationFn={api.mutate(assignPaymentMethodsToChannelDocument)}
33
+ requiredPermissions={['UpdatePaymentMethod']}
34
+ buildInput={(channelId: string) => ({
35
+ paymentMethodIds: selection.map(s => s.id),
36
+ channelId,
37
+ })}
38
+ />
39
+ );
40
+ };
41
+
42
+ export const RemovePaymentMethodsFromChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
43
+ const { selectedChannel } = useChannel();
44
+
45
+ return (
46
+ <RemoveFromChannelBulkAction
47
+ selection={selection}
48
+ table={table}
49
+ entityType="payment methods"
50
+ mutationFn={api.mutate(removePaymentMethodsFromChannelDocument)}
51
+ requiredPermissions={['UpdatePaymentMethod']}
52
+ buildInput={() => ({
53
+ paymentMethodIds: selection.map(s => s.id),
54
+ channelId: selectedChannel?.id,
55
+ })}
56
+ />
57
+ );
58
+ };
@@ -81,3 +81,30 @@ export const deletePaymentMethodDocument = graphql(`
81
81
  }
82
82
  }
83
83
  `);
84
+
85
+ export const deletePaymentMethodsDocument = graphql(`
86
+ mutation DeletePaymentMethods($ids: [ID!]!) {
87
+ deletePaymentMethods(ids: $ids) {
88
+ result
89
+ message
90
+ }
91
+ }
92
+ `);
93
+
94
+ export const assignPaymentMethodsToChannelDocument = graphql(`
95
+ mutation AssignPaymentMethodsToChannel($input: AssignPaymentMethodsToChannelInput!) {
96
+ assignPaymentMethodsToChannel(input: $input) {
97
+ id
98
+ name
99
+ }
100
+ }
101
+ `);
102
+
103
+ export const removePaymentMethodsFromChannelDocument = graphql(`
104
+ mutation RemovePaymentMethodsFromChannel($input: RemovePaymentMethodsFromChannelInput!) {
105
+ removePaymentMethodsFromChannel(input: $input) {
106
+ id
107
+ name
108
+ }
109
+ }
110
+ `);
@@ -2,10 +2,16 @@ import { BooleanDisplayBadge } from '@/components/data-display/boolean.js';
2
2
  import { DetailPageButton } from '@/components/shared/detail-page-button.js';
3
3
  import { PermissionGuard } from '@/components/shared/permission-guard.js';
4
4
  import { Button } from '@/components/ui/button.js';
5
+ import { PageActionBarRight } from '@/framework/layout-engine/page-layout.js';
5
6
  import { ListPage } from '@/framework/page/list-page.js';
6
7
  import { Trans } from '@/lib/trans.js';
7
8
  import { createFileRoute, Link } from '@tanstack/react-router';
8
9
  import { PlusIcon } from 'lucide-react';
10
+ import {
11
+ AssignPaymentMethodsToChannelBulkAction,
12
+ DeletePaymentMethodsBulkAction,
13
+ RemovePaymentMethodsFromChannelBulkAction,
14
+ } from './components/payment-method-bulk-actions.js';
9
15
  import { deletePaymentMethodDocument, paymentMethodListQuery } from './payment-methods.graphql.js';
10
16
 
11
17
  export const Route = createFileRoute('/_authenticated/_payment-methods/payment-methods')({
@@ -50,15 +56,31 @@ function PaymentMethodListPage() {
50
56
  cell: ({ row }) => <BooleanDisplayBadge value={row.original.enabled} />,
51
57
  },
52
58
  }}
59
+ bulkActions={[
60
+ {
61
+ component: AssignPaymentMethodsToChannelBulkAction,
62
+ order: 100,
63
+ },
64
+ {
65
+ component: RemovePaymentMethodsFromChannelBulkAction,
66
+ order: 200,
67
+ },
68
+ {
69
+ component: DeletePaymentMethodsBulkAction,
70
+ order: 500,
71
+ },
72
+ ]}
53
73
  >
54
- <PermissionGuard requires={['CreatePaymentMethod']}>
55
- <Button asChild>
56
- <Link to="./new">
57
- <PlusIcon className="mr-2 h-4 w-4" />
58
- New Payment Method
59
- </Link>
60
- </Button>
61
- </PermissionGuard>
74
+ <PageActionBarRight>
75
+ <PermissionGuard requires={['CreatePaymentMethod']}>
76
+ <Button asChild>
77
+ <Link to="./new">
78
+ <PlusIcon className="mr-2 h-4 w-4" />
79
+ New Payment Method
80
+ </Link>
81
+ </Button>
82
+ </PermissionGuard>
83
+ </PageActionBarRight>
62
84
  </ListPage>
63
85
  );
64
86
  }