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

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 (86) hide show
  1. package/package.json +4 -4
  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/remove-from-channel-bulk-action.tsx +89 -0
  63. package/src/lib/framework/component-registry/component-registry.tsx +31 -47
  64. package/src/lib/framework/extension-api/define-dashboard-extension.ts +29 -95
  65. package/src/lib/framework/extension-api/display-component-extensions.tsx +69 -0
  66. package/src/lib/framework/extension-api/extension-api-types.ts +18 -160
  67. package/src/lib/framework/extension-api/input-component-extensions.tsx +69 -0
  68. package/src/lib/framework/extension-api/logic/alerts.ts +10 -0
  69. package/src/lib/framework/extension-api/logic/data-table.ts +60 -0
  70. package/src/lib/framework/extension-api/logic/detail-forms.ts +48 -0
  71. package/src/lib/framework/extension-api/logic/form-components.ts +13 -0
  72. package/src/lib/framework/extension-api/logic/index.ts +8 -0
  73. package/src/lib/framework/extension-api/logic/layout.ts +22 -0
  74. package/src/lib/framework/extension-api/logic/navigation.ts +37 -0
  75. package/src/lib/framework/extension-api/logic/widgets.ts +10 -0
  76. package/src/lib/framework/extension-api/types/alerts.ts +54 -0
  77. package/src/lib/framework/extension-api/types/data-table.ts +64 -0
  78. package/src/lib/framework/extension-api/types/detail-forms.ts +81 -0
  79. package/src/lib/framework/extension-api/types/form-components.ts +32 -0
  80. package/src/lib/framework/extension-api/types/index.ts +8 -0
  81. package/src/lib/framework/extension-api/types/layout.ts +78 -0
  82. package/src/lib/framework/extension-api/types/navigation.ts +19 -0
  83. package/src/lib/framework/extension-api/types/widgets.ts +94 -0
  84. package/src/lib/framework/page/detail-page.tsx +48 -3
  85. package/src/lib/framework/registry/registry-types.ts +3 -0
  86. package/src/app/routes/_authenticated/_collections/components/assign-collections-to-channel-dialog.tsx +0 -110
@@ -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
  }
@@ -38,7 +38,10 @@ export const Route = createFileRoute('/_authenticated/_payment-methods/payment-m
38
38
  pageId,
39
39
  queryDocument: paymentMethodDetailDocument,
40
40
  breadcrumb(_isNew, entity) {
41
- return [{ path: '/payment-methods', label: 'Payment methods' }, entity?.name];
41
+ return [
42
+ { path: '/payment-methods', label: 'Payment methods' },
43
+ _isNew ? <Trans>New payment method</Trans> : entity?.name,
44
+ ];
42
45
  },
43
46
  }),
44
47
  errorComponent: ({ error }) => <ErrorPage message={error.message} />,
@@ -1,17 +1,17 @@
1
- import { useMutation } from '@tanstack/react-query';
2
- import { LayersIcon, TagIcon, TrashIcon } from 'lucide-react';
1
+ import { TagIcon } from 'lucide-react';
3
2
  import { useState } from 'react';
4
- import { toast } from 'sonner';
5
3
 
6
4
  import { DataTableBulkActionItem } from '@/components/data-table/data-table-bulk-action-item.js';
5
+ import { AssignToChannelBulkAction } from '@/components/shared/assign-to-channel-bulk-action.js';
6
+ import { usePriceFactor } from '@/components/shared/assign-to-channel-dialog.js';
7
+ import { RemoveFromChannelBulkAction } from '@/components/shared/remove-from-channel-bulk-action.js';
7
8
  import { BulkActionComponent } from '@/framework/data-table/data-table-types.js';
8
9
  import { api } from '@/graphql/api.js';
9
- import { ResultOf } from '@/graphql/graphql.js';
10
10
  import { useChannel, usePaginatedList } from '@/index.js';
11
- import { Trans, useLingui } from '@/lib/trans.js';
11
+ import { Trans } from '@/lib/trans.js';
12
+ import { DeleteBulkAction } from '../../../../common/delete-bulk-action.js';
12
13
 
13
14
  import { AssignFacetValuesDialog } from '../../_products/components/assign-facet-values-dialog.js';
14
- import { AssignToChannelDialog } from '../../_products/components/assign-to-channel-dialog.js';
15
15
  import {
16
16
  assignProductVariantsToChannelDocument,
17
17
  deleteProductVariantsDocument,
@@ -22,78 +22,34 @@ import {
22
22
  } from '../product-variants.graphql.js';
23
23
 
24
24
  export const DeleteProductVariantsBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
25
- const { refetchPaginatedList } = usePaginatedList();
26
- const { i18n } = useLingui();
27
- const { mutate } = useMutation({
28
- mutationFn: api.mutate(deleteProductVariantsDocument),
29
- onSuccess: (result: ResultOf<typeof deleteProductVariantsDocument>) => {
30
- let deleted = 0;
31
- const errors: string[] = [];
32
- for (const item of result.deleteProductVariants) {
33
- if (item.result === 'DELETED') {
34
- deleted++;
35
- } else if (item.message) {
36
- errors.push(item.message);
37
- }
38
- }
39
- if (0 < deleted) {
40
- toast.success(i18n.t(`Deleted ${deleted} product variants`));
41
- }
42
- if (0 < errors.length) {
43
- toast.error(i18n.t(`Failed to delete ${errors.length} product variants`));
44
- }
45
- refetchPaginatedList();
46
- table.resetRowSelection();
47
- },
48
- onError: () => {
49
- toast.error(`Failed to delete ${selection.length} product variants`);
50
- },
51
- });
52
25
  return (
53
- <DataTableBulkActionItem
54
- requiresPermission={['DeleteCatalog', 'DeleteProduct']}
55
- onClick={() => mutate({ ids: selection.map(s => s.id) })}
56
- label={<Trans>Delete</Trans>}
57
- confirmationText={
58
- <Trans>Are you sure you want to delete {selection.length} product variants?</Trans>
59
- }
60
- icon={TrashIcon}
61
- className="text-destructive"
26
+ <DeleteBulkAction
27
+ mutationDocument={deleteProductVariantsDocument}
28
+ entityName="product variants"
29
+ requiredPermissions={['DeleteCatalog', 'DeleteProduct']}
30
+ selection={selection}
31
+ table={table}
62
32
  />
63
33
  );
64
34
  };
65
35
 
66
36
  export const AssignProductVariantsToChannelBulkAction: BulkActionComponent<any> = ({ selection, table }) => {
67
- const { refetchPaginatedList } = usePaginatedList();
68
- const { channels } = useChannel();
69
- const [dialogOpen, setDialogOpen] = useState(false);
70
-
71
- if (channels.length < 2) {
72
- return null;
73
- }
74
-
75
- const handleSuccess = () => {
76
- refetchPaginatedList();
77
- table.resetRowSelection();
78
- };
37
+ const { priceFactor, priceFactorField } = usePriceFactor();
79
38
 
80
39
  return (
81
- <>
82
- <DataTableBulkActionItem
83
- requiresPermission={['UpdateCatalog', 'UpdateProduct']}
84
- onClick={() => setDialogOpen(true)}
85
- label={<Trans>Assign to channel</Trans>}
86
- icon={LayersIcon}
87
- />
88
- <AssignToChannelDialog
89
- open={dialogOpen}
90
- onOpenChange={setDialogOpen}
91
- entityIds={selection.map(s => s.id)}
92
- entityType="variants"
93
- mutationFn={api.mutate(assignProductVariantsToChannelDocument)}
94
- onSuccess={handleSuccess}
95
- />
96
- </>
40
+ <AssignToChannelBulkAction
41
+ selection={selection}
42
+ table={table}
43
+ entityType="variants"
44
+ mutationFn={api.mutate(assignProductVariantsToChannelDocument)}
45
+ requiredPermissions={['UpdateCatalog', 'UpdateProduct']}
46
+ buildInput={(channelId: string) => ({
47
+ productVariantIds: selection.map(s => s.id),
48
+ channelId,
49
+ priceFactor,
50
+ })}
51
+ additionalFields={priceFactorField}
52
+ />
97
53
  );
98
54
  };
99
55
 
@@ -101,49 +57,19 @@ export const RemoveProductVariantsFromChannelBulkAction: BulkActionComponent<any
101
57
  selection,
102
58
  table,
103
59
  }) => {
104
- const { refetchPaginatedList } = usePaginatedList();
105
60
  const { selectedChannel } = useChannel();
106
- const { i18n } = useLingui();
107
- const { mutate } = useMutation({
108
- mutationFn: api.mutate(removeProductVariantsFromChannelDocument),
109
- onSuccess: () => {
110
- toast.success(i18n.t(`Successfully removed ${selection.length} product variants from channel`));
111
- refetchPaginatedList();
112
- table.resetRowSelection();
113
- },
114
- onError: error => {
115
- toast.error(
116
- `Failed to remove ${selection.length} product variants from channel: ${error.message}`,
117
- );
118
- },
119
- });
120
-
121
- if (!selectedChannel) {
122
- return null;
123
- }
124
-
125
- const handleRemove = () => {
126
- mutate({
127
- input: {
128
- productVariantIds: selection.map(s => s.id),
129
- channelId: selectedChannel.id,
130
- },
131
- });
132
- };
133
61
 
134
62
  return (
135
- <DataTableBulkActionItem
136
- requiresPermission={['UpdateCatalog', 'UpdateProduct']}
137
- onClick={handleRemove}
138
- label={<Trans>Remove from current channel</Trans>}
139
- confirmationText={
140
- <Trans>
141
- Are you sure you want to remove {selection.length} product variants from the current
142
- channel?
143
- </Trans>
144
- }
145
- icon={LayersIcon}
146
- className="text-warning"
63
+ <RemoveFromChannelBulkAction
64
+ selection={selection}
65
+ table={table}
66
+ entityType="product variants"
67
+ mutationFn={api.mutate(removeProductVariantsFromChannelDocument)}
68
+ requiredPermissions={['UpdateCatalog', 'UpdateProduct']}
69
+ buildInput={() => ({
70
+ productVariantIds: selection.map(s => s.id),
71
+ channelId: selectedChannel?.id,
72
+ })}
147
73
  />
148
74
  );
149
75
  };