@pinelab/vendure-plugin-qls-fulfillment 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # 3.0.0 (2026-07-29)
2
+
3
+ - Migrated to React Dashboard.
4
+ - Added React Dashboard action bar items for product sync, order link, push order, variant link, and adding additional EANs.
5
+ - Kept legacy Angular Admin UI action bar items for backward compatibility.
6
+ - Corrected the UI permission for the "Push order to QLS" action to `QLSPushOrder`.
7
+
1
8
  # 2.3.0 (2026-08-05)
2
9
 
3
10
  - Extra prompt before adding an additional EAN
@@ -0,0 +1,121 @@
1
+ import {
2
+ api,
3
+ Button,
4
+ ConfirmationDialog,
5
+ Dialog,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ Input,
12
+ Label,
13
+ } from '@vendure/dashboard';
14
+ import { useMutation } from '@tanstack/react-query';
15
+ import { QrCode } from 'lucide-react';
16
+ import { useState } from 'react';
17
+ import { toast } from 'sonner';
18
+
19
+ import { addAdditionalEanToQlsDocument } from '../qls-dashboard.graphql';
20
+
21
+ interface AddAdditionalEanButtonProps {
22
+ context: { entity?: any };
23
+ }
24
+
25
+ /**
26
+ * Action bar button on the product variant detail page that prompts
27
+ * for an additional EAN and sends it to QLS.
28
+ *
29
+ * NOTE: This is deliberately a regular action bar button and NOT a dropdown
30
+ * (`type: 'dropdown'`) action bar item. Dropdown items are rendered inside a
31
+ * Base UI menu popup, which unmounts its children when the menu closes, and
32
+ * while the (modal) menu is open it intercepts keyboard events. Both make it
33
+ * impossible to have a working dialog with a text input inside a dropdown
34
+ * item. Vendure itself only uses button-only AlertDialogs inside dropdown
35
+ * menus; dialogs with inputs are always opened from outside a menu.
36
+ */
37
+ export function AddAdditionalEanButton({
38
+ context,
39
+ }: AddAdditionalEanButtonProps) {
40
+ const variantId = context.entity?.id;
41
+ const variantName = context.entity?.name;
42
+ const [dialogOpen, setDialogOpen] = useState(false);
43
+ const [ean, setEan] = useState('');
44
+
45
+ const { mutate: addEan, isPending } = useMutation({
46
+ mutationFn: () =>
47
+ api.mutate(addAdditionalEanToQlsDocument, {
48
+ variantId: variantId!,
49
+ additionalEANS: [ean],
50
+ }),
51
+ onSuccess: () => {
52
+ toast.success(`Added EAN ${ean} to QLS`);
53
+ setEan('');
54
+ setDialogOpen(false);
55
+ },
56
+ onError: (err: Error) => {
57
+ toast.error(err.message ?? 'Failed to add EAN to QLS');
58
+ },
59
+ });
60
+
61
+ const handleConfirm = () => {
62
+ if (!ean.trim()) {
63
+ return;
64
+ }
65
+ addEan();
66
+ };
67
+
68
+ return (
69
+ <>
70
+ <Button
71
+ variant="outline"
72
+ disabled={!variantId}
73
+ onClick={() => setDialogOpen(true)}
74
+ >
75
+ <QrCode className="mr-2 h-4 w-4" />
76
+ Add EAN
77
+ </Button>
78
+ <Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
79
+ <DialogContent>
80
+ <DialogHeader>
81
+ <DialogTitle>Add additional EAN to QLS</DialogTitle>
82
+ <DialogDescription>
83
+ Enter the additional EAN to add to this variant in QLS.
84
+ </DialogDescription>
85
+ </DialogHeader>
86
+ <div className="grid gap-4 py-4">
87
+ <div className="grid gap-2">
88
+ <Label htmlFor="ean">EAN</Label>
89
+ <Input
90
+ id="ean"
91
+ value={ean}
92
+ onChange={(e) => setEan(e.target.value)}
93
+ placeholder="EAN"
94
+ disabled={isPending}
95
+ />
96
+ </div>
97
+ </div>
98
+ <DialogFooter>
99
+ <Button
100
+ type="button"
101
+ variant="secondary"
102
+ disabled={isPending}
103
+ onClick={() => setDialogOpen(false)}
104
+ >
105
+ Cancel
106
+ </Button>
107
+ <ConfirmationDialog
108
+ title="Weet je het zeker?"
109
+ description={`Wil je EAN "${ean}" toevoegen aan product "${variantName}"?`}
110
+ onConfirm={handleConfirm}
111
+ >
112
+ <Button type="button" disabled={!ean.trim() || isPending}>
113
+ Add EAN
114
+ </Button>
115
+ </ConfirmationDialog>
116
+ </DialogFooter>
117
+ </DialogContent>
118
+ </Dialog>
119
+ </>
120
+ );
121
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ api,
3
+ Button,
4
+ Dialog,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogFooter,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DropdownMenuItem,
11
+ } from '@vendure/dashboard';
12
+ import { useQuery, useMutation } from '@tanstack/react-query';
13
+ import { RefreshCw } from 'lucide-react';
14
+ import { useState } from 'react';
15
+ import { toast } from 'sonner';
16
+
17
+ import {
18
+ orderQlsIdsDocument,
19
+ pushOrderToQlsDocument,
20
+ } from '../qls-dashboard.graphql';
21
+
22
+ interface PushOrderToQlsMenuItemProps {
23
+ context: { entity?: any };
24
+ }
25
+
26
+ /**
27
+ * Dropdown action bar item on the order detail page that pushes the order
28
+ * to QLS. If the order already exists in QLS, a confirmation dialog is shown.
29
+ */
30
+ export function PushOrderToQlsMenuItem({
31
+ context,
32
+ }: PushOrderToQlsMenuItemProps) {
33
+ const orderId = context.entity?.id;
34
+ const [confirmOpen, setConfirmOpen] = useState(false);
35
+
36
+ const { data: orderData } = useQuery({
37
+ queryKey: ['qls-order-ids', orderId],
38
+ queryFn: () => api.query(orderQlsIdsDocument, { id: orderId! }),
39
+ enabled: !!orderId,
40
+ });
41
+
42
+ const { mutate: pushOrder, isPending } = useMutation({
43
+ mutationFn: () => api.mutate(pushOrderToQlsDocument, { orderId: orderId! }),
44
+ onSuccess: (message) => {
45
+ toast.success(message ?? 'Order pushed to QLS');
46
+ setConfirmOpen(false);
47
+ },
48
+ onError: (err: Error) => {
49
+ toast.error(err.message ?? 'Failed to push order to QLS');
50
+ },
51
+ });
52
+
53
+ const handleClick = () => {
54
+ const existingIds = orderData?.order?.qlsOrderIds ?? [];
55
+ if (existingIds.length > 0) {
56
+ setConfirmOpen(true);
57
+ } else {
58
+ pushOrder();
59
+ }
60
+ };
61
+
62
+ return (
63
+ <>
64
+ <DropdownMenuItem disabled={!orderId || isPending} onClick={handleClick}>
65
+ <RefreshCw
66
+ className={`mr-2 h-4 w-4 ${isPending ? 'animate-spin' : ''}`}
67
+ />
68
+ Push order to QLS
69
+ </DropdownMenuItem>
70
+ <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
71
+ <DialogContent>
72
+ <DialogHeader>
73
+ <DialogTitle>Push order to QLS</DialogTitle>
74
+ <DialogDescription>
75
+ This order already exists in QLS. Are you sure you want to push it
76
+ again?
77
+ </DialogDescription>
78
+ </DialogHeader>
79
+ <DialogFooter>
80
+ <Button
81
+ type="button"
82
+ variant="secondary"
83
+ onClick={() => setConfirmOpen(false)}
84
+ >
85
+ Cancel
86
+ </Button>
87
+ <Button
88
+ type="button"
89
+ disabled={isPending}
90
+ onClick={() => pushOrder()}
91
+ >
92
+ Push to QLS
93
+ </Button>
94
+ </DialogFooter>
95
+ </DialogContent>
96
+ </Dialog>
97
+ </>
98
+ );
99
+ }
@@ -0,0 +1,31 @@
1
+ import { api, DropdownMenuItem } from '@vendure/dashboard';
2
+ import { useMutation } from '@tanstack/react-query';
3
+ import { RefreshCw } from 'lucide-react';
4
+ import { toast } from 'sonner';
5
+
6
+ import { triggerQlsProductSyncDocument } from '../qls-dashboard.graphql';
7
+
8
+ /**
9
+ * Dropdown action bar item on the product list that triggers a full
10
+ * product synchronization with QLS.
11
+ */
12
+ export function SyncProductsMenuItem() {
13
+ const { mutate: sync, isPending } = useMutation({
14
+ mutationFn: () => api.mutate(triggerQlsProductSyncDocument, {}),
15
+ onSuccess: () => {
16
+ toast.success('Triggered QLS full product sync...');
17
+ },
18
+ onError: (err: Error) => {
19
+ toast.error(err.message ?? 'Failed to trigger QLS product sync');
20
+ },
21
+ });
22
+
23
+ return (
24
+ <DropdownMenuItem disabled={isPending} onClick={() => sync()}>
25
+ <RefreshCw
26
+ className={`mr-2 h-4 w-4 ${isPending ? 'animate-spin' : ''}`}
27
+ />
28
+ Synchronize with QLS
29
+ </DropdownMenuItem>
30
+ );
31
+ }
@@ -0,0 +1,52 @@
1
+ import { api, Button } from '@vendure/dashboard';
2
+ import { ExternalLink } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { toast } from 'sonner';
5
+
6
+ import { orderQlsUrlDocument } from '../qls-dashboard.graphql';
7
+
8
+ interface VisitQlsOrderButtonProps {
9
+ context: { entity?: any };
10
+ }
11
+
12
+ /**
13
+ * Action bar button on the order detail page that opens the linked QLS order
14
+ * in a new tab. Only renders when the order has been placed.
15
+ */
16
+ export function VisitQlsOrderButton({ context }: VisitQlsOrderButtonProps) {
17
+ const orderId = context.entity?.id;
18
+ const orderPlacedAt = context.entity?.orderPlacedAt;
19
+ const [isPending, setIsPending] = useState(false);
20
+
21
+ if (!orderPlacedAt) {
22
+ return null;
23
+ }
24
+
25
+ const handleClick = async () => {
26
+ if (!orderId) {
27
+ toast.error('No order selected.');
28
+ return;
29
+ }
30
+ setIsPending(true);
31
+ try {
32
+ const result = await api.query(orderQlsUrlDocument, { id: orderId });
33
+ const url = result.order?.qlsOrderUrl;
34
+ if (url) {
35
+ window.open(url, '_blank');
36
+ } else {
37
+ toast.error('No QLS order found for this order.');
38
+ }
39
+ } catch (err: any) {
40
+ toast.error(err?.message ?? 'Failed to load QLS order URL');
41
+ } finally {
42
+ setIsPending(false);
43
+ }
44
+ };
45
+
46
+ return (
47
+ <Button variant="outline" disabled={isPending} onClick={handleClick}>
48
+ <ExternalLink className="mr-2 h-4 w-4" />
49
+ QLS
50
+ </Button>
51
+ );
52
+ }
@@ -0,0 +1,49 @@
1
+ import { api, Button } from '@vendure/dashboard';
2
+ import { ExternalLink } from 'lucide-react';
3
+ import { useState } from 'react';
4
+ import { toast } from 'sonner';
5
+
6
+ import { productVariantQlsUrlDocument } from '../qls-dashboard.graphql';
7
+
8
+ interface VisitQlsProductButtonProps {
9
+ context: { entity?: any };
10
+ }
11
+
12
+ /**
13
+ * Action bar button on the product variant detail page that opens the linked
14
+ * QLS product in a new tab.
15
+ */
16
+ export function VisitQlsProductButton({ context }: VisitQlsProductButtonProps) {
17
+ const variantId = context.entity?.id;
18
+ const [isPending, setIsPending] = useState(false);
19
+
20
+ const handleClick = async () => {
21
+ if (!variantId) {
22
+ toast.error('No variant selected.');
23
+ return;
24
+ }
25
+ setIsPending(true);
26
+ try {
27
+ const result = await api.query(productVariantQlsUrlDocument, {
28
+ id: variantId,
29
+ });
30
+ const url = result.productVariant?.qlsProductUrl;
31
+ if (url) {
32
+ window.open(url, '_blank');
33
+ } else {
34
+ toast.error('No QLS product found for this variant.');
35
+ }
36
+ } catch (err: any) {
37
+ toast.error(err?.message ?? 'Failed to load QLS product URL');
38
+ } finally {
39
+ setIsPending(false);
40
+ }
41
+ };
42
+
43
+ return (
44
+ <Button variant="outline" disabled={isPending} onClick={handleClick}>
45
+ <ExternalLink className="mr-2 h-4 w-4" />
46
+ QLS
47
+ </Button>
48
+ );
49
+ }
@@ -0,0 +1,48 @@
1
+ import { defineDashboardExtension } from '@vendure/dashboard';
2
+
3
+ import { AddAdditionalEanButton } from './components/AddAdditionalEanButton';
4
+ import { PushOrderToQlsMenuItem } from './components/PushOrderToQlsMenuItem';
5
+ import { SyncProductsMenuItem } from './components/SyncProductsMenuItem';
6
+ import { VisitQlsOrderButton } from './components/VisitQlsOrderButton';
7
+ import { VisitQlsProductButton } from './components/VisitQlsProductButton';
8
+
9
+ /**
10
+ * React Dashboard extension for the QLS fulfillment plugin.
11
+ *
12
+ * Replicates the legacy Angular action-bar items as dashboard action bar items.
13
+ */
14
+ defineDashboardExtension({
15
+ actionBarItems: [
16
+ {
17
+ id: 'qls-sync-products',
18
+ pageId: 'product-list',
19
+ type: 'dropdown',
20
+ requiresPermission: 'QLSFullSync',
21
+ component: () => <SyncProductsMenuItem />,
22
+ },
23
+ {
24
+ id: 'qls-visit-order',
25
+ pageId: 'order-detail',
26
+ component: ({ context }) => <VisitQlsOrderButton context={context} />,
27
+ },
28
+ {
29
+ id: 'qls-push-order',
30
+ pageId: 'order-detail',
31
+ type: 'dropdown',
32
+ requiresPermission: 'QLSPushOrder',
33
+ component: ({ context }) => <PushOrderToQlsMenuItem context={context} />,
34
+ },
35
+ {
36
+ id: 'qls-visit-product',
37
+ pageId: 'product-variant-detail',
38
+ component: ({ context }) => <VisitQlsProductButton context={context} />,
39
+ },
40
+ {
41
+ id: 'qls-add-ean',
42
+ pageId: 'product-variant-detail',
43
+ // NOTE: intentionally not `type: 'dropdown'`, see AddAdditionalEanButton
44
+ requiresPermission: 'QLSFullSync',
45
+ component: ({ context }) => <AddAdditionalEanButton context={context} />,
46
+ },
47
+ ],
48
+ });
@@ -0,0 +1,49 @@
1
+ import { graphql } from '@/gql';
2
+
3
+ export const orderQlsUrlDocument = graphql(`
4
+ query OrderQlsUrl($id: ID!) {
5
+ order(id: $id) {
6
+ id
7
+ qlsOrderUrl
8
+ }
9
+ }
10
+ `);
11
+
12
+ export const orderQlsIdsDocument = graphql(`
13
+ query OrderQlsIds($id: ID!) {
14
+ order(id: $id) {
15
+ id
16
+ qlsOrderIds
17
+ }
18
+ }
19
+ `);
20
+
21
+ export const productVariantQlsUrlDocument = graphql(`
22
+ query ProductVariantQlsUrl($id: ID!) {
23
+ productVariant(id: $id) {
24
+ id
25
+ qlsProductUrl
26
+ }
27
+ }
28
+ `);
29
+
30
+ export const triggerQlsProductSyncDocument = graphql(`
31
+ mutation TriggerQlsProductSync {
32
+ triggerQlsProductSync
33
+ }
34
+ `);
35
+
36
+ export const pushOrderToQlsDocument = graphql(`
37
+ mutation PushOrderToQls($orderId: ID!) {
38
+ pushOrderToQls(orderId: $orderId)
39
+ }
40
+ `);
41
+
42
+ export const addAdditionalEanToQlsDocument = graphql(`
43
+ mutation AddAdditionalEanToQls($variantId: ID!, $additionalEANS: [String!]!) {
44
+ addAdditionalEANSToQLS(
45
+ variantId: $variantId
46
+ additionalEANS: $additionalEANS
47
+ )
48
+ }
49
+ `);
@@ -49,6 +49,7 @@ QlsPlugin.ui = {
49
49
  exports.QlsPlugin = QlsPlugin = QlsPlugin_1 = __decorate([
50
50
  (0, core_1.VendurePlugin)({
51
51
  imports: [core_1.PluginCommonModule],
52
+ dashboard: './dashboard/index.tsx',
52
53
  providers: [
53
54
  {
54
55
  provide: constants_1.PLUGIN_INIT_OPTIONS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pinelab/vendure-plugin-qls-fulfillment",
3
- "version": "2.3.0",
3
+ "version": "3.0.0",
4
4
  "description": "Vendure plugin to fulfill orders via QLS.",
5
5
  "keywords": [
6
6
  "fulfillment",
@@ -23,7 +23,9 @@
23
23
  "CHANGELOG.md"
24
24
  ],
25
25
  "scripts": {
26
- "build": "rimraf dist && yarn generate && tsc && copyfiles -u 1 'src/ui/**/*' dist/",
26
+ "build": "rimraf dist && yarn generate && tsc && copyfiles -u 1 'src/ui/**/*' 'src/dashboard/**/*' dist/",
27
+ "build:dashboard": "vite build --config ./vite.config.mts",
28
+ "dev:dashboard": "vite --clearScreen false",
27
29
  "start": "nodemon --watch src --exec ts-node test/dev-server.ts",
28
30
  "generate": "graphql-codegen",
29
31
  "test": "vitest run --bail 1",
@@ -1,246 +0,0 @@
1
- import {
2
- addActionBarDropdownMenuItem,
3
- addActionBarItem,
4
- ModalService,
5
- } from '@vendure/admin-ui/core';
6
- import { firstValueFrom, of } from 'rxjs';
7
- import { map, catchError, take } from 'rxjs/operators';
8
- import gql from 'graphql-tag';
9
-
10
- export default [
11
- // Product sync button in product list
12
- addActionBarDropdownMenuItem({
13
- id: 'qls-fulfillment-sync',
14
- label: 'Synchronize with QLS',
15
- locationId: 'product-list',
16
- icon: 'resistor',
17
- requiresPermission: ['QLSFullSync'],
18
- onClick: (_, { dataService, notificationService }) => {
19
- dataService
20
- .mutate(
21
- gql`
22
- mutation TriggerQlsProductSync {
23
- triggerQlsProductSync
24
- }
25
- `
26
- )
27
- .subscribe({
28
- next: () => {
29
- notificationService.notify({
30
- message: `Triggered QLS full product sync...`,
31
- type: 'success',
32
- });
33
- },
34
- error: (err) => {
35
- notificationService.notify({
36
- message: err.message,
37
- type: 'error',
38
- });
39
- },
40
- });
41
- },
42
- }),
43
- // QLS order link button on order detail page
44
- addActionBarItem({
45
- id: 'qls-visit-order',
46
- label: 'QLS',
47
- locationId: 'order-detail',
48
- buttonColor: 'primary',
49
- buttonStyle: 'outline',
50
- icon: 'link',
51
- buttonState: (context) => {
52
- return context.entity$.pipe(
53
- map((entity) => ({
54
- visible: !!entity?.orderPlacedAt,
55
- disabled: false,
56
- }))
57
- );
58
- },
59
- onClick: async (event, { route, dataService, notificationService }) => {
60
- const orderId = route.snapshot.params.id;
61
- const res = await firstValueFrom(
62
- dataService.query(
63
- gql`
64
- query Order($orderId: ID!) {
65
- order(id: $orderId) {
66
- id
67
- qlsOrderUrl
68
- }
69
- }
70
- `,
71
- { orderId }
72
- ).single$
73
- );
74
- const url = (res as any).order.qlsOrderUrl;
75
- if (url) {
76
- window.open(url, '_blank');
77
- } else {
78
- notificationService.notify({
79
- message: 'No QLS order found for this order.',
80
- type: 'error',
81
- });
82
- }
83
- },
84
- }),
85
- // QLS product link button on product variant detail page
86
- addActionBarItem({
87
- id: 'qls-visit-product',
88
- label: 'QLS',
89
- locationId: 'product-variant-detail',
90
- buttonColor: 'primary',
91
- buttonStyle: 'outline',
92
- icon: 'link',
93
- onClick: async (event, { route, dataService, notificationService }) => {
94
- const variantId = route.snapshot.params.id;
95
- const res = await firstValueFrom(
96
- dataService.query(
97
- gql`
98
- query ProductVariant($id: ID!) {
99
- productVariant(id: $id) {
100
- id
101
- qlsProductUrl
102
- }
103
- }
104
- `,
105
- { id: variantId }
106
- ).single$
107
- );
108
- const url = (res as any).productVariant.qlsProductUrl;
109
- if (url) {
110
- window.open(url, '_blank');
111
- } else {
112
- notificationService.notify({
113
- message: 'No QLS product found for this variant.',
114
- type: 'error',
115
- });
116
- }
117
- },
118
- }),
119
- // Push order to QLS button on order detail page
120
- addActionBarDropdownMenuItem({
121
- id: 'qls-fulfillment-push-order',
122
- label: 'Push order to QLS',
123
- locationId: 'order-detail',
124
- icon: 'resistor',
125
- requiresPermission: ['QLSFullSync'],
126
- hasDivider: true,
127
- onClick: async (
128
- _,
129
- { route, dataService, notificationService, injector }
130
- ) => {
131
- const orderId = route.snapshot.params.id;
132
- const res = await firstValueFrom(
133
- dataService.query(
134
- gql`
135
- query Order($orderId: ID!) {
136
- order(id: $orderId) {
137
- id
138
- qlsOrderIds
139
- }
140
- }
141
- `,
142
- { orderId }
143
- ).single$
144
- );
145
- if ((res as any).order.qlsOrderIds.length > 0) {
146
- const modalService = injector.get(ModalService);
147
- const confirmed = await firstValueFrom(
148
- modalService.dialog({
149
- title: 'Push order to QLS',
150
- body: 'This order already exists in QLS. Are you sure you want to push it again?',
151
- buttons: [
152
- { type: 'secondary', label: 'Cancel', returnValue: false },
153
- { type: 'primary', label: 'Push to QLS', returnValue: true },
154
- ],
155
- })
156
- );
157
- if (!confirmed) return;
158
- }
159
-
160
- dataService
161
- .mutate(
162
- gql`
163
- mutation PushOrderToQls($orderId: ID!) {
164
- pushOrderToQls(orderId: $orderId)
165
- }
166
- `,
167
- { orderId }
168
- )
169
- .subscribe({
170
- next: (result) => {
171
- notificationService.notify({
172
- message: (result as any).pushOrderToQls,
173
- type: 'success',
174
- });
175
- },
176
- error: (err) => {
177
- notificationService.notify({
178
- message: err.message,
179
- type: 'error',
180
- });
181
- },
182
- });
183
- },
184
- }),
185
- addActionBarDropdownMenuItem({
186
- id: 'qls-fulfillment-add-ean',
187
- label: 'Add additional EAN to QLS',
188
- locationId: 'product-variant-detail',
189
- icon: 'resistor',
190
- requiresPermission: ['QLSFullSync'],
191
- onClick: async (
192
- _,
193
- { route, dataService, notificationService, entity$ }
194
- ) => {
195
- const ean = window.prompt('Enter additional EAN to add to QLS:');
196
- if (!ean) {
197
- return;
198
- }
199
- const variantId = route.snapshot.params.id;
200
- try {
201
- const variant = await firstValueFrom(entity$.pipe(take(1)));
202
- const confirmed = window.confirm(
203
- `Wil je EAN "${ean}" toevoegen aan "${variant?.name}"?`
204
- );
205
- if (!confirmed) {
206
- return;
207
- }
208
- } catch (err) {
209
- notificationService.notify({
210
- message: (err as any).message,
211
- type: 'error',
212
- });
213
- return;
214
- }
215
- dataService
216
- .mutate(
217
- gql`
218
- mutation AddAdditionalEANSToQLS(
219
- $variantId: ID!
220
- $additionalEANS: [String!]!
221
- ) {
222
- addAdditionalEANSToQLS(
223
- variantId: $variantId
224
- additionalEANS: $additionalEANS
225
- )
226
- }
227
- `,
228
- { variantId, additionalEANS: [ean] }
229
- )
230
- .subscribe({
231
- next: () => {
232
- notificationService.notify({
233
- message: `Added EAN ${ean} to QLS`,
234
- type: 'success',
235
- });
236
- },
237
- error: (err) => {
238
- notificationService.notify({
239
- message: err.message,
240
- type: 'error',
241
- });
242
- },
243
- });
244
- },
245
- }),
246
- ];