@deenruv/upsell-plugin 1.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.
Files changed (52) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +42 -0
  3. package/dist/plugin-server/api/upsell-admin.resolver.d.ts +16 -0
  4. package/dist/plugin-server/api/upsell-admin.resolver.js +58 -0
  5. package/dist/plugin-server/api/upsell-product.resolver.d.ts +7 -0
  6. package/dist/plugin-server/api/upsell-product.resolver.js +36 -0
  7. package/dist/plugin-server/constants.d.ts +1 -0
  8. package/dist/plugin-server/constants.js +1 -0
  9. package/dist/plugin-server/entities/upsell.entity.d.ts +8 -0
  10. package/dist/plugin-server/entities/upsell.entity.js +38 -0
  11. package/dist/plugin-server/extensions/upsell.extension.d.ts +3 -0
  12. package/dist/plugin-server/extensions/upsell.extension.js +26 -0
  13. package/dist/plugin-server/index.d.ts +8 -0
  14. package/dist/plugin-server/index.js +43 -0
  15. package/dist/plugin-server/services/upsell.service.d.ts +11 -0
  16. package/dist/plugin-server/services/upsell.service.js +76 -0
  17. package/dist/plugin-server/types.d.ts +1 -0
  18. package/dist/plugin-server/types.js +1 -0
  19. package/dist/plugin-server/zeus/const.d.ts +6 -0
  20. package/dist/plugin-server/zeus/const.js +4039 -0
  21. package/dist/plugin-server/zeus/index.d.ts +20455 -0
  22. package/dist/plugin-server/zeus/index.js +443 -0
  23. package/dist/plugin-ui/components/UpsellSelect.d.ts +2 -0
  24. package/dist/plugin-ui/components/UpsellSelect.js +99 -0
  25. package/dist/plugin-ui/graphql/mutations.d.ts +18 -0
  26. package/dist/plugin-ui/graphql/mutations.js +14 -0
  27. package/dist/plugin-ui/graphql/queries.d.ts +15 -0
  28. package/dist/plugin-ui/graphql/queries.js +11 -0
  29. package/dist/plugin-ui/graphql/scalars.d.ts +13 -0
  30. package/dist/plugin-ui/graphql/scalars.js +14 -0
  31. package/dist/plugin-ui/graphql/selectors.d.ts +12 -0
  32. package/dist/plugin-ui/graphql/selectors.js +8 -0
  33. package/dist/plugin-ui/index.d.ts +1 -0
  34. package/dist/plugin-ui/index.js +21 -0
  35. package/dist/plugin-ui/locales/en/index.d.ts +5 -0
  36. package/dist/plugin-ui/locales/en/index.js +2 -0
  37. package/dist/plugin-ui/locales/en/wfirma.json +4 -0
  38. package/dist/plugin-ui/locales/pl/index.d.ts +5 -0
  39. package/dist/plugin-ui/locales/pl/index.js +2 -0
  40. package/dist/plugin-ui/locales/pl/wfirma.json +4 -0
  41. package/dist/plugin-ui/pages/ExtrasPage.d.ts +2 -0
  42. package/dist/plugin-ui/pages/ExtrasPage.js +8 -0
  43. package/dist/plugin-ui/translation-ns.d.ts +1 -0
  44. package/dist/plugin-ui/translation-ns.js +1 -0
  45. package/dist/plugin-ui/tsconfig.json +18 -0
  46. package/dist/plugin-ui/zeus/const.d.ts +6 -0
  47. package/dist/plugin-ui/zeus/const.js +4039 -0
  48. package/dist/plugin-ui/zeus/index.d.ts +20455 -0
  49. package/dist/plugin-ui/zeus/index.js +459 -0
  50. package/dist/plugin-ui/zeus/typedDocumentNode.d.ts +3 -0
  51. package/dist/plugin-ui/zeus/typedDocumentNode.js +9 -0
  52. package/package.json +61 -0
@@ -0,0 +1,443 @@
1
+ /* eslint-disable */
2
+ import { AllTypesProps, ReturnTypes, Ops } from './const.js';
3
+ export const HOST = "http://localhost:3000/admin-api";
4
+ export const HEADERS = {};
5
+ export const apiSubscription = (options) => (query) => {
6
+ var _a, _b, _c;
7
+ try {
8
+ const queryString = options[0] + '?query=' + encodeURIComponent(query);
9
+ const wsString = queryString.replace('http', 'ws');
10
+ const host = (options.length > 1 && ((_b = (_a = options[1]) === null || _a === void 0 ? void 0 : _a.websocket) === null || _b === void 0 ? void 0 : _b[0])) || wsString;
11
+ const webSocketOptions = ((_c = options[1]) === null || _c === void 0 ? void 0 : _c.websocket) || [host];
12
+ const ws = new WebSocket(...webSocketOptions);
13
+ return {
14
+ ws,
15
+ on: (e) => {
16
+ ws.onmessage = (event) => {
17
+ if (event.data) {
18
+ const parsed = JSON.parse(event.data);
19
+ const data = parsed.data;
20
+ return e(data);
21
+ }
22
+ };
23
+ },
24
+ off: (e) => {
25
+ ws.onclose = e;
26
+ },
27
+ error: (e) => {
28
+ ws.onerror = e;
29
+ },
30
+ open: (e) => {
31
+ ws.onopen = e;
32
+ },
33
+ };
34
+ }
35
+ catch (_d) {
36
+ throw new Error('No websockets implemented');
37
+ }
38
+ };
39
+ const handleFetchResponse = (response) => {
40
+ if (!response.ok) {
41
+ return new Promise((_, reject) => {
42
+ response
43
+ .text()
44
+ .then((text) => {
45
+ try {
46
+ reject(JSON.parse(text));
47
+ }
48
+ catch (err) {
49
+ reject(text);
50
+ }
51
+ })
52
+ .catch(reject);
53
+ });
54
+ }
55
+ return response.json();
56
+ };
57
+ export const apiFetch = (options) => (query, variables = {}) => {
58
+ const fetchOptions = options[1] || {};
59
+ if (fetchOptions.method && fetchOptions.method === 'GET') {
60
+ return fetch(`${options[0]}?query=${encodeURIComponent(query)}`, fetchOptions)
61
+ .then(handleFetchResponse)
62
+ .then((response) => {
63
+ if (response.errors) {
64
+ throw new GraphQLError(response);
65
+ }
66
+ return response.data;
67
+ });
68
+ }
69
+ return fetch(`${options[0]}`, Object.assign({ body: JSON.stringify({ query, variables }), method: 'POST', headers: {
70
+ 'Content-Type': 'application/json',
71
+ } }, fetchOptions))
72
+ .then(handleFetchResponse)
73
+ .then((response) => {
74
+ if (response.errors) {
75
+ throw new GraphQLError(response);
76
+ }
77
+ return response.data;
78
+ });
79
+ };
80
+ export const InternalsBuildQuery = ({ ops, props, returns, options, scalars, }) => {
81
+ const ibb = (k, o, p = '', root = true, vars = []) => {
82
+ var _a;
83
+ const keyForPath = purifyGraphQLKey(k);
84
+ const newPath = [p, keyForPath].join(SEPARATOR);
85
+ if (!o) {
86
+ return '';
87
+ }
88
+ if (typeof o === 'boolean' || typeof o === 'number') {
89
+ return k;
90
+ }
91
+ if (typeof o === 'string') {
92
+ return `${k} ${o}`;
93
+ }
94
+ if (Array.isArray(o)) {
95
+ const args = InternalArgsBuilt({
96
+ props,
97
+ returns,
98
+ ops,
99
+ scalars,
100
+ vars,
101
+ })(o[0], newPath);
102
+ return `${ibb(args ? `${k}(${args})` : k, o[1], p, false, vars)}`;
103
+ }
104
+ if (k === '__alias') {
105
+ return Object.entries(o)
106
+ .map(([alias, objectUnderAlias]) => {
107
+ if (typeof objectUnderAlias !== 'object' || Array.isArray(objectUnderAlias)) {
108
+ throw new Error('Invalid alias it should be __alias:{ YOUR_ALIAS_NAME: { OPERATION_NAME: { ...selectors }}}');
109
+ }
110
+ const operationName = Object.keys(objectUnderAlias)[0];
111
+ const operation = objectUnderAlias[operationName];
112
+ return ibb(`${alias}:${operationName}`, operation, p, false, vars);
113
+ })
114
+ .join('\n');
115
+ }
116
+ const hasOperationName = root && (options === null || options === void 0 ? void 0 : options.operationName) ? ' ' + options.operationName : '';
117
+ const keyForDirectives = (_a = o.__directives) !== null && _a !== void 0 ? _a : '';
118
+ const query = `{${Object.entries(o)
119
+ .filter(([k]) => k !== '__directives')
120
+ .map((e) => ibb(...e, [p, `field<>${keyForPath}`].join(SEPARATOR), false, vars))
121
+ .join('\n')}}`;
122
+ if (!root) {
123
+ return `${k} ${keyForDirectives}${hasOperationName} ${query}`;
124
+ }
125
+ const varsString = vars.map((v) => `${v.name}: ${v.graphQLType}`).join(', ');
126
+ return `${k} ${keyForDirectives}${hasOperationName}${varsString ? `(${varsString})` : ''} ${query}`;
127
+ };
128
+ return ibb;
129
+ };
130
+ export const Thunder = (fn, thunderGraphQLOptions) => (operation, graphqlOptions) => (o, ops) => {
131
+ const options = Object.assign(Object.assign({}, thunderGraphQLOptions), graphqlOptions);
132
+ return fn(Zeus(operation, o, {
133
+ operationOptions: ops,
134
+ scalars: options === null || options === void 0 ? void 0 : options.scalars,
135
+ }), ops === null || ops === void 0 ? void 0 : ops.variables).then((data) => {
136
+ if (options === null || options === void 0 ? void 0 : options.scalars) {
137
+ return decodeScalarsInResponse({
138
+ response: data,
139
+ initialOp: operation,
140
+ initialZeusQuery: o,
141
+ returns: ReturnTypes,
142
+ scalars: options.scalars,
143
+ ops: Ops,
144
+ });
145
+ }
146
+ return data;
147
+ });
148
+ };
149
+ export const Chain = (...options) => Thunder(apiFetch(options));
150
+ export const SubscriptionThunder = (fn, thunderGraphQLOptions) => (operation, graphqlOptions) => (o, ops) => {
151
+ const options = Object.assign(Object.assign({}, thunderGraphQLOptions), graphqlOptions);
152
+ const returnedFunction = fn(Zeus(operation, o, {
153
+ operationOptions: ops,
154
+ scalars: options === null || options === void 0 ? void 0 : options.scalars,
155
+ }));
156
+ if ((returnedFunction === null || returnedFunction === void 0 ? void 0 : returnedFunction.on) && (options === null || options === void 0 ? void 0 : options.scalars)) {
157
+ const wrapped = returnedFunction.on;
158
+ returnedFunction.on = (fnToCall) => wrapped((data) => {
159
+ if (options === null || options === void 0 ? void 0 : options.scalars) {
160
+ return fnToCall(decodeScalarsInResponse({
161
+ response: data,
162
+ initialOp: operation,
163
+ initialZeusQuery: o,
164
+ returns: ReturnTypes,
165
+ scalars: options.scalars,
166
+ ops: Ops,
167
+ }));
168
+ }
169
+ return fnToCall(data);
170
+ });
171
+ }
172
+ return returnedFunction;
173
+ };
174
+ export const Subscription = (...options) => SubscriptionThunder(apiSubscription(options));
175
+ export const Zeus = (operation, o, ops) => InternalsBuildQuery({
176
+ props: AllTypesProps,
177
+ returns: ReturnTypes,
178
+ ops: Ops,
179
+ options: ops === null || ops === void 0 ? void 0 : ops.operationOptions,
180
+ scalars: ops === null || ops === void 0 ? void 0 : ops.scalars,
181
+ })(operation, o);
182
+ export const ZeusSelect = () => ((t) => t);
183
+ export const Selector = (key) => key && ZeusSelect();
184
+ export const TypeFromSelector = (key) => key && ZeusSelect();
185
+ export const Gql = Chain(HOST, {
186
+ headers: Object.assign({ 'Content-Type': 'application/json' }, HEADERS),
187
+ });
188
+ export const ZeusScalars = ZeusSelect();
189
+ export const decodeScalarsInResponse = ({ response, scalars, returns, ops, initialZeusQuery, initialOp, }) => {
190
+ if (!scalars) {
191
+ return response;
192
+ }
193
+ const builder = PrepareScalarPaths({
194
+ ops,
195
+ returns,
196
+ });
197
+ const scalarPaths = builder(initialOp, ops[initialOp], initialZeusQuery);
198
+ if (scalarPaths) {
199
+ const r = traverseResponse({ scalarPaths, resolvers: scalars })(initialOp, response, [ops[initialOp]]);
200
+ return r;
201
+ }
202
+ return response;
203
+ };
204
+ export const traverseResponse = ({ resolvers, scalarPaths, }) => {
205
+ const ibb = (k, o, p = []) => {
206
+ var _a;
207
+ if (Array.isArray(o)) {
208
+ return o.map((eachO) => ibb(k, eachO, p));
209
+ }
210
+ if (o == null) {
211
+ return o;
212
+ }
213
+ const scalarPathString = p.join(SEPARATOR);
214
+ const currentScalarString = scalarPaths[scalarPathString];
215
+ if (currentScalarString) {
216
+ const currentDecoder = (_a = resolvers[currentScalarString.split('.')[1]]) === null || _a === void 0 ? void 0 : _a.decode;
217
+ if (currentDecoder) {
218
+ return currentDecoder(o);
219
+ }
220
+ }
221
+ if (typeof o === 'boolean' || typeof o === 'number' || typeof o === 'string' || !o) {
222
+ return o;
223
+ }
224
+ const entries = Object.entries(o).map(([k, v]) => [k, ibb(k, v, [...p, purifyGraphQLKey(k)])]);
225
+ const objectFromEntries = entries.reduce((a, [k, v]) => {
226
+ a[k] = v;
227
+ return a;
228
+ }, {});
229
+ return objectFromEntries;
230
+ };
231
+ return ibb;
232
+ };
233
+ export const SEPARATOR = '|';
234
+ export class GraphQLError extends Error {
235
+ constructor(response) {
236
+ super('');
237
+ this.response = response;
238
+ console.error(response);
239
+ }
240
+ toString() {
241
+ return 'GraphQL Response Error';
242
+ }
243
+ }
244
+ const ExtractScalar = (mappedParts, returns) => {
245
+ if (mappedParts.length === 0) {
246
+ return;
247
+ }
248
+ const oKey = mappedParts[0];
249
+ const returnP1 = returns[oKey];
250
+ if (typeof returnP1 === 'object') {
251
+ const returnP2 = returnP1[mappedParts[1]];
252
+ if (returnP2) {
253
+ return ExtractScalar([returnP2, ...mappedParts.slice(2)], returns);
254
+ }
255
+ return undefined;
256
+ }
257
+ return returnP1;
258
+ };
259
+ export const PrepareScalarPaths = ({ ops, returns }) => {
260
+ const ibb = (k, originalKey, o, p = [], pOriginals = [], root = true) => {
261
+ if (!o) {
262
+ return;
263
+ }
264
+ if (typeof o === 'boolean' || typeof o === 'number' || typeof o === 'string') {
265
+ const extractionArray = [...pOriginals, originalKey];
266
+ const isScalar = ExtractScalar(extractionArray, returns);
267
+ if (isScalar === null || isScalar === void 0 ? void 0 : isScalar.startsWith('scalar')) {
268
+ const partOfTree = {
269
+ [[...p, k].join(SEPARATOR)]: isScalar,
270
+ };
271
+ return partOfTree;
272
+ }
273
+ return {};
274
+ }
275
+ if (Array.isArray(o)) {
276
+ return ibb(k, k, o[1], p, pOriginals, false);
277
+ }
278
+ if (k === '__alias') {
279
+ return Object.entries(o)
280
+ .map(([alias, objectUnderAlias]) => {
281
+ if (typeof objectUnderAlias !== 'object' || Array.isArray(objectUnderAlias)) {
282
+ throw new Error('Invalid alias it should be __alias:{ YOUR_ALIAS_NAME: { OPERATION_NAME: { ...selectors }}}');
283
+ }
284
+ const operationName = Object.keys(objectUnderAlias)[0];
285
+ const operation = objectUnderAlias[operationName];
286
+ return ibb(alias, operationName, operation, p, pOriginals, false);
287
+ })
288
+ .reduce((a, b) => (Object.assign(Object.assign({}, a), b)));
289
+ }
290
+ const keyName = root ? ops[k] : k;
291
+ return Object.entries(o)
292
+ .filter(([k]) => k !== '__directives')
293
+ .map(([k, v]) => {
294
+ // Inline fragments shouldn't be added to the path as they aren't a field
295
+ const isInlineFragment = originalKey.match(/^...\s*on/) != null;
296
+ return ibb(k, k, v, isInlineFragment ? p : [...p, purifyGraphQLKey(keyName || k)], isInlineFragment ? pOriginals : [...pOriginals, purifyGraphQLKey(originalKey)], false);
297
+ })
298
+ .reduce((a, b) => (Object.assign(Object.assign({}, a), b)));
299
+ };
300
+ return ibb;
301
+ };
302
+ export const purifyGraphQLKey = (k) => k.replace(/\([^)]*\)/g, '').replace(/^[^:]*\:/g, '');
303
+ const mapPart = (p) => {
304
+ const [isArg, isField] = p.split('<>');
305
+ if (isField) {
306
+ return {
307
+ v: isField,
308
+ __type: 'field',
309
+ };
310
+ }
311
+ return {
312
+ v: isArg,
313
+ __type: 'arg',
314
+ };
315
+ };
316
+ export const ResolveFromPath = (props, returns, ops) => {
317
+ const ResolvePropsType = (mappedParts) => {
318
+ const oKey = ops[mappedParts[0].v];
319
+ const propsP1 = oKey ? props[oKey] : props[mappedParts[0].v];
320
+ if (propsP1 === 'enum' && mappedParts.length === 1) {
321
+ return 'enum';
322
+ }
323
+ if (typeof propsP1 === 'string' && propsP1.startsWith('scalar.') && mappedParts.length === 1) {
324
+ return propsP1;
325
+ }
326
+ if (typeof propsP1 === 'object') {
327
+ if (mappedParts.length < 2) {
328
+ return 'not';
329
+ }
330
+ const propsP2 = propsP1[mappedParts[1].v];
331
+ if (typeof propsP2 === 'string') {
332
+ return rpp(`${propsP2}${SEPARATOR}${mappedParts
333
+ .slice(2)
334
+ .map((mp) => mp.v)
335
+ .join(SEPARATOR)}`);
336
+ }
337
+ if (typeof propsP2 === 'object') {
338
+ if (mappedParts.length < 3) {
339
+ return 'not';
340
+ }
341
+ const propsP3 = propsP2[mappedParts[2].v];
342
+ if (propsP3 && mappedParts[2].__type === 'arg') {
343
+ return rpp(`${propsP3}${SEPARATOR}${mappedParts
344
+ .slice(3)
345
+ .map((mp) => mp.v)
346
+ .join(SEPARATOR)}`);
347
+ }
348
+ }
349
+ }
350
+ };
351
+ const ResolveReturnType = (mappedParts) => {
352
+ if (mappedParts.length === 0) {
353
+ return 'not';
354
+ }
355
+ const oKey = ops[mappedParts[0].v];
356
+ const returnP1 = oKey ? returns[oKey] : returns[mappedParts[0].v];
357
+ if (typeof returnP1 === 'object') {
358
+ if (mappedParts.length < 2)
359
+ return 'not';
360
+ const returnP2 = returnP1[mappedParts[1].v];
361
+ if (returnP2) {
362
+ return rpp(`${returnP2}${SEPARATOR}${mappedParts
363
+ .slice(2)
364
+ .map((mp) => mp.v)
365
+ .join(SEPARATOR)}`);
366
+ }
367
+ }
368
+ };
369
+ const rpp = (path) => {
370
+ const parts = path.split(SEPARATOR).filter((l) => l.length > 0);
371
+ const mappedParts = parts.map(mapPart);
372
+ const propsP1 = ResolvePropsType(mappedParts);
373
+ if (propsP1) {
374
+ return propsP1;
375
+ }
376
+ const returnP1 = ResolveReturnType(mappedParts);
377
+ if (returnP1) {
378
+ return returnP1;
379
+ }
380
+ return 'not';
381
+ };
382
+ return rpp;
383
+ };
384
+ export const InternalArgsBuilt = ({ props, ops, returns, scalars, vars, }) => {
385
+ const arb = (a, p = '', root = true) => {
386
+ var _a, _b;
387
+ if (typeof a === 'string') {
388
+ if (a.startsWith(START_VAR_NAME)) {
389
+ const [varName, graphQLType] = a.replace(START_VAR_NAME, '$').split(GRAPHQL_TYPE_SEPARATOR);
390
+ const v = vars.find((v) => v.name === varName);
391
+ if (!v) {
392
+ vars.push({
393
+ name: varName,
394
+ graphQLType,
395
+ });
396
+ }
397
+ else {
398
+ if (v.graphQLType !== graphQLType) {
399
+ throw new Error(`Invalid variable exists with two different GraphQL Types, "${v.graphQLType}" and ${graphQLType}`);
400
+ }
401
+ }
402
+ return varName;
403
+ }
404
+ }
405
+ const checkType = ResolveFromPath(props, returns, ops)(p);
406
+ if (checkType.startsWith('scalar.')) {
407
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
408
+ const [_, ...splittedScalar] = checkType.split('.');
409
+ const scalarKey = splittedScalar.join('.');
410
+ return ((_b = (_a = scalars === null || scalars === void 0 ? void 0 : scalars[scalarKey]) === null || _a === void 0 ? void 0 : _a.encode) === null || _b === void 0 ? void 0 : _b.call(_a, a)) || JSON.stringify(a);
411
+ }
412
+ if (Array.isArray(a)) {
413
+ return `[${a.map((arr) => arb(arr, p, false)).join(', ')}]`;
414
+ }
415
+ if (typeof a === 'string') {
416
+ if (checkType === 'enum') {
417
+ return a;
418
+ }
419
+ return `${JSON.stringify(a)}`;
420
+ }
421
+ if (typeof a === 'object') {
422
+ if (a === null) {
423
+ return `null`;
424
+ }
425
+ const returnedObjectString = Object.entries(a)
426
+ .filter(([, v]) => typeof v !== 'undefined')
427
+ .map(([k, v]) => `${k}: ${arb(v, [p, k].join(SEPARATOR), false)}`)
428
+ .join(',\n');
429
+ if (!root) {
430
+ return `{${returnedObjectString}}`;
431
+ }
432
+ return returnedObjectString;
433
+ }
434
+ return `${a}`;
435
+ };
436
+ return arb;
437
+ };
438
+ export const resolverFor = (type, field, fn) => fn;
439
+ export const START_VAR_NAME = `$ZEUS_VAR`;
440
+ export const GRAPHQL_TYPE_SEPARATOR = `__$GRAPHQL__`;
441
+ export const $ = (name, graphqlType) => {
442
+ return (START_VAR_NAME + name + GRAPHQL_TYPE_SEPARATOR + graphqlType);
443
+ };
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const UpsellSelect: () => React.JSX.Element;
@@ -0,0 +1,99 @@
1
+ "use client";
2
+ import { buttonVariants, Card, CardContent, CardHeader, DialogProductPicker, Routes, ScrollArea, useDetailView, useLazyQuery, useMutation, } from "@deenruv/react-ui-devkit";
3
+ import React, { useEffect, useState } from "react";
4
+ import { useNavigate } from "react-router-dom";
5
+ import { QUERIES } from "../graphql/queries";
6
+ import { MUTATIONS } from "../graphql/mutations";
7
+ import { toast } from "sonner";
8
+ import { Loader2, PlusCircle, XIcon } from "lucide-react";
9
+ export const UpsellSelect = () => {
10
+ const { id } = useDetailView("products-detail-view");
11
+ const navigate = useNavigate();
12
+ const [run, { data, loading }] = useLazyQuery(QUERIES["GET_UPSELLS"]);
13
+ const [createUpsell, { loading: createLoading }] = useMutation(MUTATIONS["CREATE_UPSELL"]);
14
+ const [deleteUpsell, { loading: deleteLoading }] = useMutation(MUTATIONS["DELETE_UPSELL"]);
15
+ const [deletingIds, setDeletingIds] = useState([]);
16
+ const onSingleDelete = async (upsellProductID) => {
17
+ if (!id) {
18
+ toast.error("No product selected");
19
+ return;
20
+ }
21
+ setDeletingIds((prev) => [...prev, upsellProductID]);
22
+ try {
23
+ await deleteUpsell({
24
+ input: [{ baseProductID: id, upsellProductID }],
25
+ });
26
+ run({ productID: id });
27
+ toast.success("Upsell product removed");
28
+ }
29
+ catch {
30
+ toast.error("Failed to delete upsell product");
31
+ }
32
+ finally {
33
+ setDeletingIds((prev) => prev.filter((id) => id !== upsellProductID));
34
+ }
35
+ };
36
+ useEffect(() => {
37
+ if (id)
38
+ run({ productID: id });
39
+ }, [id]);
40
+ return (React.createElement(Card, { className: "border border-border shadow-sm" },
41
+ React.createElement(CardHeader, { className: "border-b border-border bg-muted/30" },
42
+ React.createElement("div", { className: "flex items-center justify-between" },
43
+ React.createElement("h3", { className: "text-lg font-semibold" }, "Upsell Products"),
44
+ React.createElement(DialogProductPicker, { initialValue: data?.upsellProducts.map((p) => p.id) || [], mode: "product", multiple: true, onSubmit: async (result) => {
45
+ if (!id) {
46
+ toast.error("No product selected");
47
+ return;
48
+ }
49
+ if (!result) {
50
+ toast.info("Canceled select upsell products");
51
+ return;
52
+ }
53
+ const selectedIds = result.map((p) => p.productId);
54
+ const toDelete = data?.upsellProducts
55
+ .filter((p) => !selectedIds.includes(p.id))
56
+ .map((p) => p.id);
57
+ const toAdd = selectedIds.filter((id) => data?.upsellProducts.every((p) => p.id !== id));
58
+ try {
59
+ if (toDelete?.length) {
60
+ await deleteUpsell({
61
+ input: toDelete.map((upsellProductID) => ({
62
+ baseProductID: id,
63
+ upsellProductID,
64
+ })),
65
+ });
66
+ }
67
+ if (toAdd.length) {
68
+ await createUpsell({
69
+ input: toAdd.map((upsellProductID) => ({
70
+ baseProductID: id,
71
+ upsellProductID,
72
+ })),
73
+ });
74
+ }
75
+ toast.success("Upsell products updated successfully");
76
+ }
77
+ catch {
78
+ toast.error("Failed to update upsell products");
79
+ }
80
+ run({ productID: id });
81
+ } }))),
82
+ React.createElement(CardContent, { className: "p-6" }, loading ? (React.createElement("div", { className: "flex h-[400px] items-center justify-center" },
83
+ React.createElement(Loader2, { className: "h-8 w-8 animate-spin text-muted-foreground" }))) : (React.createElement(ScrollArea, { className: "h-[400px]" }, data?.upsellProducts?.length ? (React.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4" }, data.upsellProducts.map((product) => (React.createElement("div", { key: product.id, className: "group relative flex flex-col overflow-hidden rounded-lg border border-border bg-card transition-all hover:shadow-md" },
84
+ React.createElement("div", { className: "relative aspect-square overflow-hidden bg-muted/20" },
85
+ product.featuredAsset?.preview ? (React.createElement("img", { src: product.featuredAsset.preview || "/placeholder.svg", alt: product.name, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" })) : (React.createElement("div", { className: "flex h-full w-full items-center justify-center bg-muted/30 text-muted-foreground" }, "No image")),
86
+ React.createElement("button", { className: "absolute right-2 top-2 rounded-full bg-background/80 p-1.5 text-destructive opacity-0 shadow-sm transition-opacity hover:bg-destructive hover:text-destructive-foreground focus:opacity-100 group-hover:opacity-100", onClick: () => onSingleDelete(product.id), disabled: deletingIds.includes(product.id), "aria-label": "Remove upsell product" }, deletingIds.includes(product.id) ? (React.createElement(Loader2, { className: "h-4 w-4 animate-spin" })) : (React.createElement(XIcon, { className: "h-4 w-4" })))),
87
+ React.createElement("div", { className: "flex flex-1 flex-col p-3" },
88
+ React.createElement("h4", { className: "line-clamp-2 font-medium leading-tight text-sm mb-3", title: product.name }, product.name),
89
+ React.createElement("div", { className: "mt-auto" },
90
+ React.createElement("a", { href: Routes.products.to(product.id), target: "_blank", className: buttonVariants({
91
+ variant: "secondary",
92
+ size: "sm",
93
+ className: "w-full text-xs",
94
+ }), rel: "noreferrer" }, "View Product")))))))) : (React.createElement("div", { className: "flex h-[300px] flex-col items-center justify-center rounded-lg border border-dashed border-muted-foreground/20 p-8 text-center" },
95
+ React.createElement("div", { className: "rounded-full bg-muted/30 p-3" },
96
+ React.createElement(PlusCircle, { className: "h-6 w-6 text-muted-foreground" })),
97
+ React.createElement("h3", { className: "mt-4 text-lg font-medium" }, "No upsell products"),
98
+ React.createElement("p", { className: "mt-2 text-sm text-muted-foreground" }, "Add upsell products to increase average order value"))))))));
99
+ };
@@ -0,0 +1,18 @@
1
+ export declare const MUTATIONS: {
2
+ CREATE_UPSELL: import("@graphql-typed-document-node/core").TypedDocumentNode<{
3
+ createUpsell: boolean;
4
+ }, {} & {
5
+ input: {
6
+ baseProductID: string | import("../zeus").Variable<any, string>;
7
+ upsellProductID: string | import("../zeus").Variable<any, string>;
8
+ }[];
9
+ }>;
10
+ DELETE_UPSELL: import("@graphql-typed-document-node/core").TypedDocumentNode<{
11
+ deleteUpsell: boolean;
12
+ }, {} & {
13
+ input: {
14
+ baseProductID: string | import("../zeus").Variable<any, string>;
15
+ upsellProductID: string | import("../zeus").Variable<any, string>;
16
+ }[];
17
+ }>;
18
+ };
@@ -0,0 +1,14 @@
1
+ import { $ } from "../zeus";
2
+ import { typedGql } from "../zeus/typedDocumentNode";
3
+ import { scalars } from "./scalars";
4
+ const mutation = typedGql("mutation", { scalars });
5
+ const CREATE_UPSELL = mutation({
6
+ createUpsell: [{ input: $("input", "[UpsellInput!]!") }, true],
7
+ });
8
+ const DELETE_UPSELL = mutation({
9
+ deleteUpsell: [{ input: $("input", "[UpsellInput!]!") }, true],
10
+ });
11
+ export const MUTATIONS = {
12
+ CREATE_UPSELL,
13
+ DELETE_UPSELL,
14
+ };
@@ -0,0 +1,15 @@
1
+ export declare const QUERIES: {
2
+ GET_UPSELLS: import("@graphql-typed-document-node/core").TypedDocumentNode<{
3
+ upsellProducts: {
4
+ id: string;
5
+ createdAt: string;
6
+ updatedAt: string;
7
+ name: string;
8
+ featuredAsset?: {
9
+ preview: string;
10
+ } | undefined;
11
+ }[];
12
+ }, {} & {
13
+ productID: {};
14
+ }>;
15
+ };
@@ -0,0 +1,11 @@
1
+ import { typedGql } from "../zeus/typedDocumentNode";
2
+ import { $ } from "../zeus";
3
+ import { scalars } from "./scalars";
4
+ import { upsellSelector } from "./selectors";
5
+ const query = typedGql("query", { scalars });
6
+ const GET_UPSELLS = query({
7
+ upsellProducts: [{ productID: $("productID", "ID!") }, upsellSelector],
8
+ });
9
+ export const QUERIES = {
10
+ GET_UPSELLS,
11
+ };
@@ -0,0 +1,13 @@
1
+ export declare const scalars: {
2
+ DateTime: {
3
+ decode: (e: unknown) => string;
4
+ encode: (e: unknown) => string;
5
+ };
6
+ JSON: {
7
+ decode: (e: unknown) => any;
8
+ encode: (e: unknown) => string;
9
+ };
10
+ Money: {
11
+ decode: (e: unknown) => number;
12
+ };
13
+ };
@@ -0,0 +1,14 @@
1
+ import { ZeusScalars } from '../zeus';
2
+ export const scalars = ZeusScalars({
3
+ DateTime: {
4
+ decode: (e) => new Date(e).toISOString(),
5
+ encode: (e) => e.toISOString(),
6
+ },
7
+ JSON: {
8
+ decode: (e) => JSON.parse(e),
9
+ encode: (e) => JSON.stringify(e),
10
+ },
11
+ Money: {
12
+ decode: e => e,
13
+ },
14
+ });
@@ -0,0 +1,12 @@
1
+ import { FromSelector } from "../zeus";
2
+ import { scalars } from "./scalars";
3
+ export declare const upsellSelector: {
4
+ id: true;
5
+ createdAt: true;
6
+ updatedAt: true;
7
+ name: true;
8
+ featuredAsset: {
9
+ preview: true;
10
+ };
11
+ };
12
+ export type UpsellType = FromSelector<typeof upsellSelector, "Product", typeof scalars>;
@@ -0,0 +1,8 @@
1
+ import { Selector } from "../zeus";
2
+ export const upsellSelector = Selector("Product")({
3
+ id: true,
4
+ createdAt: true,
5
+ updatedAt: true,
6
+ name: true,
7
+ featuredAsset: { preview: true },
8
+ });
@@ -0,0 +1 @@
1
+ export declare const UpsellUIPlugin: import("@deenruv/react-ui-devkit").DeenruvUIPlugin<Record<string, any>>;