react-shopwave-connect 0.4.1 → 0.4.3
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/README.md +17 -5
- package/dist/core/index.cjs +119 -48
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +80 -9
- package/dist/core/index.d.ts +80 -9
- package/dist/core/index.js +116 -49
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs +129 -44
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +21 -1
- package/dist/hooks/index.d.ts +21 -1
- package/dist/hooks/index.js +128 -45
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +148 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +100 -9
- package/dist/index.d.ts +100 -9
- package/dist/index.js +143 -49
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +83 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +63 -7
- package/dist/next/index.d.ts +63 -7
- package/dist/next/index.js +83 -3
- package/dist/next/index.js.map +1 -1
- package/dist/server/index.cjs +80 -2
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +60 -7
- package/dist/server/index.d.ts +60 -7
- package/dist/server/index.js +80 -3
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -375,6 +375,106 @@ async function fetchProductsMap(productIds, options = {}, batchSize = 200) {
|
|
|
375
375
|
return result;
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/core/resources.ts
|
|
380
|
+
/**
|
|
381
|
+
* - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
|
|
382
|
+
* updates it (without an `id` the API would create a second merchant, so the
|
|
383
|
+
* route requires one).
|
|
384
|
+
* - user: `GET /user` (read-only here; the API's `POST /user` invites users and
|
|
385
|
+
* needs the admin scope).
|
|
386
|
+
*/
|
|
387
|
+
const SHOPWAVE_RESOURCES = Object.freeze({
|
|
388
|
+
merchant: {
|
|
389
|
+
route: "merchant",
|
|
390
|
+
key: "merchant",
|
|
391
|
+
upstream: "merchant",
|
|
392
|
+
writable: true
|
|
393
|
+
},
|
|
394
|
+
user: {
|
|
395
|
+
route: "user",
|
|
396
|
+
key: "user",
|
|
397
|
+
upstream: "user",
|
|
398
|
+
writable: false
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
/** `UploadKind` → the API's `contentType` value. */
|
|
402
|
+
const UPLOAD_KINDS = Object.freeze({
|
|
403
|
+
merchant: "merchant",
|
|
404
|
+
merchantFeature: "merchantFeature",
|
|
405
|
+
product: "product",
|
|
406
|
+
user: "user",
|
|
407
|
+
applicationLogo: "applicaionLogo",
|
|
408
|
+
applicationImages: "applicaionImages"
|
|
409
|
+
});
|
|
410
|
+
/** How uploads are addressed (app route and Shopwave uploader). */
|
|
411
|
+
const SHOPWAVE_UPLOAD = Object.freeze({
|
|
412
|
+
route: "upload",
|
|
413
|
+
upstream: "uploader",
|
|
414
|
+
kindHeader: "contentType",
|
|
415
|
+
fileField: "file",
|
|
416
|
+
kindField: "kind"
|
|
417
|
+
});
|
|
418
|
+
function isUploadKind(value) {
|
|
419
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
|
|
420
|
+
}
|
|
421
|
+
const SHOPWAVE_LOOKUPS = Object.freeze({
|
|
422
|
+
productTags: {
|
|
423
|
+
route: "products/tags",
|
|
424
|
+
key: "tags",
|
|
425
|
+
upstream: "product/tags"
|
|
426
|
+
},
|
|
427
|
+
productInstanceTags: {
|
|
428
|
+
route: "products/instance-tags",
|
|
429
|
+
key: "tags",
|
|
430
|
+
upstream: "product/instance/tags"
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
//#endregion
|
|
435
|
+
//#region src/core/productTags.ts
|
|
436
|
+
/**
|
|
437
|
+
* Turns a tag list from the API into clean strings: trims each tag, drops
|
|
438
|
+
* blanks and anything that isn't text, and removes exact duplicates (first one
|
|
439
|
+
* wins). The API's own order is kept. A map is accepted too (its values are
|
|
440
|
+
* used), in case the API ever answers keyed by id.
|
|
441
|
+
*/
|
|
442
|
+
function normalizeTagList(value) {
|
|
443
|
+
const list = Array.isArray(value) ? value : value && typeof value === "object" ? Object.values(value) : [];
|
|
444
|
+
const seen = /* @__PURE__ */ new Set();
|
|
445
|
+
const out = [];
|
|
446
|
+
for (const item of list) {
|
|
447
|
+
if (typeof item !== "string" && typeof item !== "number") continue;
|
|
448
|
+
const tag = String(item).trim();
|
|
449
|
+
if (!tag || seen.has(tag)) continue;
|
|
450
|
+
seen.add(tag);
|
|
451
|
+
out.push(tag);
|
|
452
|
+
}
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
async function fetchLookup(kind, options) {
|
|
456
|
+
const def = SHOPWAVE_LOOKUPS[kind];
|
|
457
|
+
if (!def) throw new Error(`Unknown Shopwave lookup "${kind}"`);
|
|
458
|
+
const json = await apiGet(`/api/${def.route}`, {}, options);
|
|
459
|
+
assertNoApiErrors(json, 200);
|
|
460
|
+
return normalizeTagList(json?.[def.key]);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Every distinct tag used on the merchant's products (`GET /product/tags`),
|
|
464
|
+
* for a tag field's suggestions. Returns `[]` when there are none.
|
|
465
|
+
*/
|
|
466
|
+
function fetchProductTags(options = {}) {
|
|
467
|
+
return fetchLookup("productTags", options);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Every distinct tag used on the merchant's price instances
|
|
471
|
+
* (`GET /product/instance/tags`): the channel names (Eat In, Takeout, …).
|
|
472
|
+
* Returns `[]` when there are none.
|
|
473
|
+
*/
|
|
474
|
+
function fetchProductInstanceTags(options = {}) {
|
|
475
|
+
return fetchLookup("productInstanceTags", options);
|
|
476
|
+
}
|
|
477
|
+
|
|
378
478
|
//#endregion
|
|
379
479
|
//#region src/core/promotion.ts
|
|
380
480
|
/** Promotion rule identifiers as stored by the Shopwave API. */
|
|
@@ -509,7 +609,10 @@ async function submitEntity({ endpoint, method = "POST", payload }, options = {}
|
|
|
509
609
|
* (https://developer.merchantstack.com/api-reference.html) and checked against
|
|
510
610
|
* the live API where noted:
|
|
511
611
|
* - category/product/store: GET filtered by `<x>Ids`, POST upsert, DELETE with `<x>Id` → 205.
|
|
512
|
-
* - promotion: no DELETE;
|
|
612
|
+
* - promotion: no DELETE; promotions are never deleted, only expired. Expiring
|
|
613
|
+
* reads the promotion and saves the whole record back with `endDate` = now (and
|
|
614
|
+
* `startDate` = now too if it hadn't started); the live API rejects a partial
|
|
615
|
+
* `{ id, endDate }` save with error 899. Dates are UTC `YYYY-MM-DD HH:mm:ss`.
|
|
513
616
|
* - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
|
|
514
617
|
* changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
|
|
515
618
|
* - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
|
|
@@ -550,7 +653,9 @@ const SHOPWAVE_ENTITIES = Object.freeze({
|
|
|
550
653
|
idHeader: "promotionId",
|
|
551
654
|
writable: true,
|
|
552
655
|
deleteMode: "retire",
|
|
553
|
-
retireField: "endDate"
|
|
656
|
+
retireField: "endDate",
|
|
657
|
+
retireWithRecord: true,
|
|
658
|
+
retireStartField: "startDate"
|
|
554
659
|
},
|
|
555
660
|
employee: {
|
|
556
661
|
route: "employees",
|
|
@@ -625,7 +730,8 @@ async function saveEntity(kind, item, options = {}) {
|
|
|
625
730
|
/**
|
|
626
731
|
* Deletes one entity (a soft delete: it stays readable with `deleted: true`).
|
|
627
732
|
* Employees and promotions can't be deleted in Shopwave; for them the route
|
|
628
|
-
* sets `exitDate` / `endDate` to now instead (see `SHOPWAVE_ENTITIES`)
|
|
733
|
+
* sets `exitDate` / `endDate` to now instead (see `SHOPWAVE_ENTITIES`), and a
|
|
734
|
+
* promotion is saved back whole (see {@link deletePromotion}).
|
|
629
735
|
*
|
|
630
736
|
* Shopwave answers 205 with an empty body — also for ids that don't exist — so
|
|
631
737
|
* a resolved promise means "the API accepted the request", not "a record was
|
|
@@ -666,7 +772,12 @@ const saveStore = (store, options) => saveEntity("store", store, options);
|
|
|
666
772
|
const deleteStore = (id, options) => deleteEntityById("store", id, options);
|
|
667
773
|
const fetchStore = (id, params, options) => fetchEntityById("store", id, params, options);
|
|
668
774
|
const savePromotion = (promotion, options) => saveEntity("promotion", promotion, options);
|
|
669
|
-
/**
|
|
775
|
+
/**
|
|
776
|
+
* Expires a promotion (Shopwave never deletes promotions). The route reads the
|
|
777
|
+
* promotion and saves it back with `endDate` set to now; a promotion that hasn't
|
|
778
|
+
* started yet gets `startDate` = `endDate` = now. Rejects if the promotion doesn't
|
|
779
|
+
* exist or the save fails.
|
|
780
|
+
*/
|
|
670
781
|
const deletePromotion = (id, options) => deleteEntityById("promotion", id, options);
|
|
671
782
|
const fetchPromotion = (id, params, options) => fetchEntityById("promotion", id, params, options);
|
|
672
783
|
/**
|
|
@@ -825,50 +936,6 @@ function buildConsumerMap(consumers) {
|
|
|
825
936
|
return map;
|
|
826
937
|
}
|
|
827
938
|
|
|
828
|
-
//#endregion
|
|
829
|
-
//#region src/core/resources.ts
|
|
830
|
-
/**
|
|
831
|
-
* - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
|
|
832
|
-
* updates it (without an `id` the API would create a second merchant, so the
|
|
833
|
-
* route requires one).
|
|
834
|
-
* - user: `GET /user` (read-only here; the API's `POST /user` invites users and
|
|
835
|
-
* needs the admin scope).
|
|
836
|
-
*/
|
|
837
|
-
const SHOPWAVE_RESOURCES = Object.freeze({
|
|
838
|
-
merchant: {
|
|
839
|
-
route: "merchant",
|
|
840
|
-
key: "merchant",
|
|
841
|
-
upstream: "merchant",
|
|
842
|
-
writable: true
|
|
843
|
-
},
|
|
844
|
-
user: {
|
|
845
|
-
route: "user",
|
|
846
|
-
key: "user",
|
|
847
|
-
upstream: "user",
|
|
848
|
-
writable: false
|
|
849
|
-
}
|
|
850
|
-
});
|
|
851
|
-
/** `UploadKind` → the API's `contentType` value. */
|
|
852
|
-
const UPLOAD_KINDS = Object.freeze({
|
|
853
|
-
merchant: "merchant",
|
|
854
|
-
merchantFeature: "merchantFeature",
|
|
855
|
-
product: "product",
|
|
856
|
-
user: "user",
|
|
857
|
-
applicationLogo: "applicaionLogo",
|
|
858
|
-
applicationImages: "applicaionImages"
|
|
859
|
-
});
|
|
860
|
-
/** How uploads are addressed (app route and Shopwave uploader). */
|
|
861
|
-
const SHOPWAVE_UPLOAD = Object.freeze({
|
|
862
|
-
route: "upload",
|
|
863
|
-
upstream: "uploader",
|
|
864
|
-
kindHeader: "contentType",
|
|
865
|
-
fileField: "file",
|
|
866
|
-
kindField: "kind"
|
|
867
|
-
});
|
|
868
|
-
function isUploadKind(value) {
|
|
869
|
-
return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
939
|
//#endregion
|
|
873
940
|
//#region src/core/imageFit.ts
|
|
874
941
|
/**
|
|
@@ -1535,6 +1602,33 @@ function useProduct(params = {}, options) {
|
|
|
1535
1602
|
]);
|
|
1536
1603
|
}
|
|
1537
1604
|
|
|
1605
|
+
//#endregion
|
|
1606
|
+
//#region src/hooks/useProductTags.ts
|
|
1607
|
+
/**
|
|
1608
|
+
* The merchant's distinct product tags, for a tag field's suggestions.
|
|
1609
|
+
* `data` is `string[]` once loaded.
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* const [opened, setOpened] = useState(false);
|
|
1613
|
+
* const { data: tags, loading } = useProductTags({ enabled: opened });
|
|
1614
|
+
*/
|
|
1615
|
+
function useProductTags(options) {
|
|
1616
|
+
return useQuery((signal) => fetchProductTags({
|
|
1617
|
+
...options,
|
|
1618
|
+
signal
|
|
1619
|
+
}), [options?.baseUrl, options?.token], options?.enabled ?? true);
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* The merchant's distinct price-instance (channel) tags: Eat In, Takeout, …
|
|
1623
|
+
* `data` is `string[]` once loaded.
|
|
1624
|
+
*/
|
|
1625
|
+
function useProductInstanceTags(options) {
|
|
1626
|
+
return useQuery((signal) => fetchProductInstanceTags({
|
|
1627
|
+
...options,
|
|
1628
|
+
signal
|
|
1629
|
+
}), [options?.baseUrl, options?.token], options?.enabled ?? true);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1538
1632
|
//#endregion
|
|
1539
1633
|
//#region src/hooks/usePromotion.ts
|
|
1540
1634
|
/**
|
|
@@ -1858,6 +1952,7 @@ exports.MERCHANT_READ_ONLY_FIELDS = MERCHANT_READ_ONLY_FIELDS;
|
|
|
1858
1952
|
exports.PromotionRuleSet = PromotionRuleSet;
|
|
1859
1953
|
exports.SHOPWAVE_ENTITIES = SHOPWAVE_ENTITIES;
|
|
1860
1954
|
exports.SHOPWAVE_IMAGE_BASE_URL = SHOPWAVE_IMAGE_BASE_URL;
|
|
1955
|
+
exports.SHOPWAVE_LOOKUPS = SHOPWAVE_LOOKUPS;
|
|
1861
1956
|
exports.SHOPWAVE_RESOURCES = SHOPWAVE_RESOURCES;
|
|
1862
1957
|
exports.SHOPWAVE_UPLOAD = SHOPWAVE_UPLOAD;
|
|
1863
1958
|
exports.ShopwaveApiError = ShopwaveApiError;
|
|
@@ -1891,6 +1986,8 @@ exports.fetchEmployees = fetchEmployees;
|
|
|
1891
1986
|
exports.fetchEntityById = fetchEntityById;
|
|
1892
1987
|
exports.fetchMerchant = fetchMerchant;
|
|
1893
1988
|
exports.fetchProduct = fetchProduct;
|
|
1989
|
+
exports.fetchProductInstanceTags = fetchProductInstanceTags;
|
|
1990
|
+
exports.fetchProductTags = fetchProductTags;
|
|
1894
1991
|
exports.fetchProducts = fetchProducts;
|
|
1895
1992
|
exports.fetchProductsMap = fetchProductsMap;
|
|
1896
1993
|
exports.fetchPromotion = fetchPromotion;
|
|
@@ -1917,6 +2014,7 @@ exports.logoutPath = logoutPath;
|
|
|
1917
2014
|
exports.merchantLinksFromForm = merchantLinksFromForm;
|
|
1918
2015
|
exports.merchantLinksToForm = merchantLinksToForm;
|
|
1919
2016
|
exports.normalizeMerchantLinks = normalizeMerchantLinks;
|
|
2017
|
+
exports.normalizeTagList = normalizeTagList;
|
|
1920
2018
|
exports.parseBasketReportData = parseBasketReportData;
|
|
1921
2019
|
exports.readImageSize = readImageSize;
|
|
1922
2020
|
exports.rolesById = rolesById;
|
|
@@ -1943,6 +2041,8 @@ exports.useEmployee = useEmployee;
|
|
|
1943
2041
|
exports.useLogout = useLogout;
|
|
1944
2042
|
exports.useMerchant = useMerchant;
|
|
1945
2043
|
exports.useProduct = useProduct;
|
|
2044
|
+
exports.useProductInstanceTags = useProductInstanceTags;
|
|
2045
|
+
exports.useProductTags = useProductTags;
|
|
1946
2046
|
exports.usePromotion = usePromotion;
|
|
1947
2047
|
exports.useReport = useReport;
|
|
1948
2048
|
exports.useSession = useSession;
|