react-shopwave-connect 0.4.5 → 0.4.7
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 +19 -11
- package/dist/core/index.cjs +281 -137
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +307 -160
- package/dist/core/index.d.ts +307 -160
- package/dist/core/index.js +276 -138
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs +268 -115
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +96 -29
- package/dist/hooks/index.d.ts +96 -29
- package/dist/hooks/index.js +268 -116
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +301 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +316 -160
- package/dist/index.d.ts +316 -160
- package/dist/index.js +295 -138
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +69 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +36 -5
- package/dist/next/index.d.ts +36 -5
- package/dist/next/index.js +69 -2
- package/dist/next/index.js.map +1 -1
- package/dist/server/index.cjs +70 -2
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +45 -8
- package/dist/server/index.d.ts +45 -8
- package/dist/server/index.js +70 -3
- package/dist/server/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -187,12 +187,15 @@ async function apiSend(path, method, payload, options = {}) {
|
|
|
187
187
|
/**
|
|
188
188
|
* DELETE helper. Shopwave answers deletes with 205 and an empty body; if a body
|
|
189
189
|
* does come back and carries API errors, they are thrown. Returns the parsed
|
|
190
|
-
* body (`null` when empty).
|
|
190
|
+
* body (`null` when empty). `extras`, when given, travel in the `extras` header
|
|
191
|
+
* (the server forwards only keys its delete route allows).
|
|
191
192
|
*/
|
|
192
|
-
async function apiDelete(path, options = {}) {
|
|
193
|
+
async function apiDelete(path, options = {}, extras) {
|
|
194
|
+
const headers = baseHeaders(options);
|
|
195
|
+
if (extras) headers.extras = JSON.stringify(buildExtras(extras));
|
|
193
196
|
const { status, body } = await send(path, {
|
|
194
197
|
method: "DELETE",
|
|
195
|
-
headers
|
|
198
|
+
headers
|
|
196
199
|
}, options);
|
|
197
200
|
if (getApiErrorMap(body)) throw toShopwaveApiError(status, body);
|
|
198
201
|
return body;
|
|
@@ -463,133 +466,6 @@ function fetchProductInstanceTags(options = {}) {
|
|
|
463
466
|
return fetchLookup("productInstanceTags", options);
|
|
464
467
|
}
|
|
465
468
|
|
|
466
|
-
//#endregion
|
|
467
|
-
//#region src/core/promotion.ts
|
|
468
|
-
/** Promotion rule identifiers as stored by the Shopwave API. */
|
|
469
|
-
let PromotionRuleSet = /* @__PURE__ */ function(PromotionRuleSet$1) {
|
|
470
|
-
PromotionRuleSet$1["Percentage"] = "|%^/";
|
|
471
|
-
PromotionRuleSet$1["Combo"] = "&%</";
|
|
472
|
-
PromotionRuleSet$1["MealDeal"] = "&=^/";
|
|
473
|
-
return PromotionRuleSet$1;
|
|
474
|
-
}({});
|
|
475
|
-
/**
|
|
476
|
-
* Fetches promotions. Returns the flattened `Promotion[]`.
|
|
477
|
-
*/
|
|
478
|
-
async function fetchPromotions(params = {}, options = {}) {
|
|
479
|
-
const extras = { deleted: params.deleted ?? false };
|
|
480
|
-
if (params.promotionIds) extras.promotionIds = params.promotionIds;
|
|
481
|
-
if (params.imminent !== void 0) extras.imminent = params.imminent;
|
|
482
|
-
if (params.active !== void 0) extras.active = params.active;
|
|
483
|
-
if (params.expired !== void 0) extras.expired = params.expired;
|
|
484
|
-
const json = await apiGet("/api/promotions", extras, withToken(options, params.token));
|
|
485
|
-
assertNoApiErrors(json, 200);
|
|
486
|
-
return Object.values(json?.promotions ?? {});
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
//#endregion
|
|
490
|
-
//#region src/core/store.ts
|
|
491
|
-
/**
|
|
492
|
-
* Fetches stores. Returns the flattened `Store[]`.
|
|
493
|
-
*/
|
|
494
|
-
async function fetchStores(params = {}, options = {}) {
|
|
495
|
-
const extras = { deleted: params.deleted ?? false };
|
|
496
|
-
if (params.storeIds) extras.storeIds = params.storeIds;
|
|
497
|
-
const json = await apiGet("/api/stores", extras, withToken(options, params.token));
|
|
498
|
-
assertNoApiErrors(json, 200);
|
|
499
|
-
return Object.values(json?.stores ?? {});
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
//#endregion
|
|
503
|
-
//#region src/core/report.ts
|
|
504
|
-
/**
|
|
505
|
-
* Runs one or more report queries and returns the `reports` map keyed by query
|
|
506
|
-
* name. The query is JSON-encoded into the `extras.query` header, matching the
|
|
507
|
-
* original endpoint contract.
|
|
508
|
-
*/
|
|
509
|
-
async function fetchReport(query, options = {}) {
|
|
510
|
-
const json = await apiGet("/api/report", { query: JSON.stringify(query) }, options);
|
|
511
|
-
assertNoApiErrors(json, 200);
|
|
512
|
-
return json?.reports ?? {};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
//#endregion
|
|
516
|
-
//#region src/core/session.ts
|
|
517
|
-
/**
|
|
518
|
-
* Reads the current login status from `GET /api/session`.
|
|
519
|
-
*
|
|
520
|
-
* Works with the new `react-shopwave-connect/next` session route
|
|
521
|
-
* (`{ loggedIn }`) and with older routes that returned the raw session
|
|
522
|
-
* (`{ token: {...} }`) — in that case only a boolean is surfaced.
|
|
523
|
-
*/
|
|
524
|
-
async function fetchSession(options = {}) {
|
|
525
|
-
const doFetch = options.fetch ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
526
|
-
if (!doFetch) throw new Error("No fetch implementation available. Pass `options.fetch`.");
|
|
527
|
-
const response = await doFetch(`${options.baseUrl ?? ""}/api/session`, {
|
|
528
|
-
method: "GET",
|
|
529
|
-
headers: { Accept: "application/json" },
|
|
530
|
-
cache: "no-store",
|
|
531
|
-
credentials: "same-origin",
|
|
532
|
-
signal: options.signal
|
|
533
|
-
});
|
|
534
|
-
if (response.status === 401) return { loggedIn: false };
|
|
535
|
-
if (!response.ok) throw new Error(`Session request failed (${response.status})`);
|
|
536
|
-
const json = await response.json();
|
|
537
|
-
if (json && typeof json.loggedIn === "boolean") return {
|
|
538
|
-
loggedIn: json.loggedIn,
|
|
539
|
-
expiresAt: typeof json.expiresAt === "number" ? json.expiresAt : void 0
|
|
540
|
-
};
|
|
541
|
-
return { loggedIn: Boolean(json && json.token) };
|
|
542
|
-
}
|
|
543
|
-
/**
|
|
544
|
-
* Path that starts a login and returns to `returnTo` afterwards.
|
|
545
|
-
* Navigate the browser to it (`window.location.href = loginPath(...)`).
|
|
546
|
-
*/
|
|
547
|
-
function loginPath(returnTo, authPath = "/auth") {
|
|
548
|
-
return returnTo ? `${authPath}?returnTo=${encodeURIComponent(returnTo)}` : authPath;
|
|
549
|
-
}
|
|
550
|
-
/** Path that logs out of the app and the Shopwave auth server. */
|
|
551
|
-
function logoutPath(authPath = "/auth") {
|
|
552
|
-
return `${authPath}/logout`;
|
|
553
|
-
}
|
|
554
|
-
/**
|
|
555
|
-
* Ends the current server session (no redirect).
|
|
556
|
-
*
|
|
557
|
-
* Prefer navigating to {@link logoutPath} when using
|
|
558
|
-
* `react-shopwave-connect/next`: it also signs the user out of the auth server.
|
|
559
|
-
*/
|
|
560
|
-
async function logout(options = {}) {
|
|
561
|
-
await apiDelete("/api/session?action=logout", options);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
//#endregion
|
|
565
|
-
//#region src/core/entity.ts
|
|
566
|
-
/**
|
|
567
|
-
* Low-level, untyped mutations: the caller supplies the route and payload.
|
|
568
|
-
*
|
|
569
|
-
* Prefer the typed functions in `./entities` (`saveProduct`, `deleteCategory`,
|
|
570
|
-
* …): they know each route, build the request envelope, check the response and
|
|
571
|
-
* return the saved entity with its id. Keep these for routes that have no typed
|
|
572
|
-
* function yet (e.g. `/api/merchant`).
|
|
573
|
-
*/
|
|
574
|
-
/**
|
|
575
|
-
* Deletes `/api/{resourcePath}/{entityId}`.
|
|
576
|
-
*/
|
|
577
|
-
async function deleteEntity(resourcePath, entityId, options = {}) {
|
|
578
|
-
if (entityId == null || entityId === "") throw new Error("entityId is missing");
|
|
579
|
-
await apiDelete(`/api/${resourcePath}/${entityId}`, options);
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* Sends a create/update mutation. Returns the response's `result` field when it
|
|
583
|
-
* has one (older app routes wrapped their answer that way), otherwise the whole
|
|
584
|
-
* parsed response body. Throws `ShopwaveApiError` on HTTP or API errors.
|
|
585
|
-
*/
|
|
586
|
-
async function submitEntity({ endpoint, method = "POST", payload }, options = {}) {
|
|
587
|
-
const json = await apiSend(endpoint, method, payload, options);
|
|
588
|
-
assertNoApiErrors(json, 200);
|
|
589
|
-
if (json && typeof json === "object" && "result" in json) return json.result;
|
|
590
|
-
return json ?? void 0;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
469
|
//#endregion
|
|
594
470
|
//#region src/core/imageFit.ts
|
|
595
471
|
/**
|
|
@@ -876,7 +752,7 @@ const STATIC_HOST = /^https?:\/\/static\.merchantstack\.com\/images\//i;
|
|
|
876
752
|
/**
|
|
877
753
|
* Turns an image path from the API into a URL a page can load over HTTPS:
|
|
878
754
|
* `http://static.merchantstack.com/images/x.png` → `<base>/x.png`, and a
|
|
879
|
-
* relative path (`merchant/
|
|
755
|
+
* relative path (`merchant/123/logo/x.png`) → `<base>/merchant/123/logo/x.png`.
|
|
880
756
|
* Other absolute URLs are returned unchanged; empty input gives `""`.
|
|
881
757
|
*/
|
|
882
758
|
function getImageUrl(path, base = SHOPWAVE_IMAGE_BASE_URL) {
|
|
@@ -928,7 +804,7 @@ const MERCHANT_IMAGE_SLOT_KEYS = [
|
|
|
928
804
|
"featured"
|
|
929
805
|
];
|
|
930
806
|
/**
|
|
931
|
-
* Where each slot lives, its upload kind and its exact size (from
|
|
807
|
+
* Where each slot lives, its upload kind and its exact size (from the existing admin's
|
|
932
808
|
* merchant settings page).
|
|
933
809
|
*/
|
|
934
810
|
const MERCHANT_IMAGE_SLOTS = Object.freeze({
|
|
@@ -1019,7 +895,7 @@ function toStored(value) {
|
|
|
1019
895
|
}
|
|
1020
896
|
/**
|
|
1021
897
|
* The `imageIds` to send on a save: every URL is turned back into its file
|
|
1022
|
-
* name (what
|
|
898
|
+
* name (what the existing admin does with images you didn't change) and empty values
|
|
1023
899
|
* are dropped. No images gives `{}`.
|
|
1024
900
|
*/
|
|
1025
901
|
function toStoredImageIds(imageIds) {
|
|
@@ -1040,7 +916,7 @@ async function uploadMerchantImage(file, slot, options = {}) {
|
|
|
1040
916
|
//#endregion
|
|
1041
917
|
//#region src/core/productImages.ts
|
|
1042
918
|
/**
|
|
1043
|
-
* Product images, as the live API stores them (
|
|
919
|
+
* Product images, as the live API stores them (4 Nov 2026):
|
|
1044
920
|
* - A product's `images` is a list. **Reads return full URLs**
|
|
1045
921
|
* (`http://static.merchantstack.com/images/merchant/<id>/product/<file>`);
|
|
1046
922
|
* **writes take the uploader's file names** ({@link toStoredProductImages};
|
|
@@ -1085,6 +961,27 @@ function toStoredProductImages(images) {
|
|
|
1085
961
|
* - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
|
|
1086
962
|
* changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
|
|
1087
963
|
* - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
|
|
964
|
+
* - productComponent: modifier groups, `/product/component` (live, staging, 11 Nov 2026). The
|
|
965
|
+
* API reference lists no read filters; these are what the live API does:
|
|
966
|
+
* - Reads filter by `componentIds` (group ids) or `productIds` (the parent product; option
|
|
967
|
+
* products don't match). `productComponentIds` is ignored on reads: every group comes back.
|
|
968
|
+
* `deleted: false` gives live groups only, `true` deleted groups only, no header both.
|
|
969
|
+
* No match → 204 with an empty body. No order, sort, priority or position field anywhere.
|
|
970
|
+
* - Groups are keyed by id; `products` (option products) by product id; `productInstances`
|
|
971
|
+
* (option rows) by row id. Reads reformat values: price `"100"` → `"100.0"`,
|
|
972
|
+
* vatPercentage `"0.2000"` → `"0.200"`. `tags` and `name` are stored as sent (no trimming;
|
|
973
|
+
* a `tags` array is joined with commas; `tags` null or left out → null).
|
|
974
|
+
* - POST upserts (201, echoed under the ref). For each option product in `products`, its rows
|
|
975
|
+
* are replaced: every row sent is created afresh with a new id (a row `id` is ignored, also
|
|
976
|
+
* on a price change) and rows left out are removed. Option products left out of the save
|
|
977
|
+
* are untouched, and an empty `productInstances` removes nothing. A save without `products`
|
|
978
|
+
* updates only the group's own fields. The echo isn't proof of what was stored: renames of
|
|
979
|
+
* groups created moments earlier by a test run echoed the new name but never stuck (re-read
|
|
980
|
+
* for 90 s), while older groups, and groups created in Shopwave's own admin, renamed at
|
|
981
|
+
* once. Cause not yet known.
|
|
982
|
+
* - DELETE with `productComponentIds` (plural, comma list) → 205, soft delete. Adding
|
|
983
|
+
* `productComponentInstanceIds` deletes just those rows (205); they stay in live reads with
|
|
984
|
+
* `deleteDate` set. The singular `productComponentId` hangs (504 after 60 s).
|
|
1088
985
|
*/
|
|
1089
986
|
const SHOPWAVE_ENTITIES = Object.freeze({
|
|
1090
987
|
product: {
|
|
@@ -1144,6 +1041,16 @@ const SHOPWAVE_ENTITIES = Object.freeze({
|
|
|
1144
1041
|
idHeader: "consumerId",
|
|
1145
1042
|
writable: false,
|
|
1146
1043
|
deleteMode: "none"
|
|
1044
|
+
},
|
|
1045
|
+
productComponent: {
|
|
1046
|
+
route: "product-components",
|
|
1047
|
+
collection: "productComponents",
|
|
1048
|
+
upstream: "product/component",
|
|
1049
|
+
idsHeader: "componentIds",
|
|
1050
|
+
idHeader: "productComponentIds",
|
|
1051
|
+
writable: true,
|
|
1052
|
+
deleteMode: "delete",
|
|
1053
|
+
childIdsHeader: "productComponentInstanceIds"
|
|
1147
1054
|
}
|
|
1148
1055
|
});
|
|
1149
1056
|
function definition(kind) {
|
|
@@ -1269,6 +1176,237 @@ const deleteEmployee = (id, options) => deleteEntityById("employee", id, options
|
|
|
1269
1176
|
const fetchEmployee = (id, params, options) => fetchEntityById("employee", id, params, options);
|
|
1270
1177
|
const fetchConsumer = (id, params, options) => fetchEntityById("consumer", id, params, options);
|
|
1271
1178
|
|
|
1179
|
+
//#endregion
|
|
1180
|
+
//#region src/core/productComponent.ts
|
|
1181
|
+
/** Row fields the server sets; never sent back. */
|
|
1182
|
+
const SERVER_ROW_FIELDS = [
|
|
1183
|
+
"id",
|
|
1184
|
+
"activeDate",
|
|
1185
|
+
"deleteDate",
|
|
1186
|
+
"timestamp"
|
|
1187
|
+
];
|
|
1188
|
+
const isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1189
|
+
function listOf(value) {
|
|
1190
|
+
if (!value) return [];
|
|
1191
|
+
if (Array.isArray(value)) return value.map((v) => [null, v]);
|
|
1192
|
+
return Object.entries(value);
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Builds the save body for a product component from a group as read (or as a form holds it):
|
|
1196
|
+
* - `products` keyed by product id (a product without `id` takes its key);
|
|
1197
|
+
* - each product's rows keyed `"0"`, `"1"`, …, like every other ref in a save;
|
|
1198
|
+
* - rows that have a `deleteDate` are left out: the API recreates every row it's sent, so
|
|
1199
|
+
* sending one back would bring a deleted option back as a new row;
|
|
1200
|
+
* - server row fields (`id`, `activeDate`, `deleteDate`, `timestamp`) are removed, since the
|
|
1201
|
+
* API gives every row it's sent a new id anyway;
|
|
1202
|
+
* - a `tags` list becomes a comma string.
|
|
1203
|
+
*
|
|
1204
|
+
* The API replaces the rows of every option product in the save and leaves the others alone,
|
|
1205
|
+
* so send all the rows to keep for each product included. To remove an option product
|
|
1206
|
+
* entirely, delete its rows with {@link deleteProductComponentInstances}.
|
|
1207
|
+
*/
|
|
1208
|
+
function toProductComponentForSave(input) {
|
|
1209
|
+
const { products,...group } = input;
|
|
1210
|
+
if (products == null) return { ...group };
|
|
1211
|
+
const out = {};
|
|
1212
|
+
for (const [key, product] of listOf(products)) {
|
|
1213
|
+
if (!isObject(product)) continue;
|
|
1214
|
+
const id = product.id ?? key;
|
|
1215
|
+
if (id == null || String(id).trim() === "") continue;
|
|
1216
|
+
const { productInstances,...rest } = product;
|
|
1217
|
+
const rows = {};
|
|
1218
|
+
let index = 0;
|
|
1219
|
+
for (const [, row] of listOf(productInstances)) {
|
|
1220
|
+
if (!isObject(row) || row.deleteDate) continue;
|
|
1221
|
+
const copy = { ...row };
|
|
1222
|
+
for (const field of SERVER_ROW_FIELDS) delete copy[field];
|
|
1223
|
+
if (Array.isArray(copy.tags)) copy.tags = copy.tags.map(String).join(",");
|
|
1224
|
+
rows[String(index++)] = copy;
|
|
1225
|
+
}
|
|
1226
|
+
out[String(id)] = {
|
|
1227
|
+
...rest,
|
|
1228
|
+
id,
|
|
1229
|
+
productInstances: rows
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
return {
|
|
1233
|
+
...group,
|
|
1234
|
+
products: out
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Reads product components, optionally filtered by parent product and/or group id.
|
|
1239
|
+
* With no filter it returns every group on the merchant. Returns `[]` when nothing matches
|
|
1240
|
+
* (the API answers 204), and when a filter list is empty (no request is made).
|
|
1241
|
+
*
|
|
1242
|
+
* Rows deleted with {@link deleteProductComponentInstances} are still returned, with
|
|
1243
|
+
* `deleteDate` set; skip them when listing options.
|
|
1244
|
+
*/
|
|
1245
|
+
async function fetchProductComponents(params = {}, options = {}) {
|
|
1246
|
+
const def = SHOPWAVE_ENTITIES.productComponent;
|
|
1247
|
+
if (params.productIds?.length === 0 || params.productComponentIds?.length === 0) return [];
|
|
1248
|
+
const extras = { deleted: params.deleted ?? false };
|
|
1249
|
+
if (params.productIds) extras.productIds = params.productIds;
|
|
1250
|
+
if (params.productComponentIds) extras[def.idsHeader] = params.productComponentIds;
|
|
1251
|
+
const json = await apiGet(`/api/${def.route}`, extras, options);
|
|
1252
|
+
assertNoApiErrors(json, 200);
|
|
1253
|
+
const map = json?.[def.collection];
|
|
1254
|
+
return isObject(map) ? Object.values(map) : [];
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Reads one product component by id, or `null` when there's no such live group.
|
|
1258
|
+
* Note that `{ deleted: true }` returns the group only if it *has* been deleted.
|
|
1259
|
+
*/
|
|
1260
|
+
const fetchProductComponent = (id, params, options) => fetchEntityById("productComponent", id, params, options);
|
|
1261
|
+
/**
|
|
1262
|
+
* Creates (no `id`) or updates (with `id`) a product component and returns it with its id.
|
|
1263
|
+
* The body is built with {@link toProductComponentForSave}. Every option row sent is stored
|
|
1264
|
+
* under a new id, so re-read with {@link fetchProductComponent} for the stored rows.
|
|
1265
|
+
*/
|
|
1266
|
+
const saveProductComponent = (input, options) => saveEntity("productComponent", toProductComponentForSave(input), options);
|
|
1267
|
+
/** Deletes a product component (soft delete: `deleted: true` reads still return it). */
|
|
1268
|
+
const deleteProductComponent = (id, options) => deleteEntityById("productComponent", id, options);
|
|
1269
|
+
/**
|
|
1270
|
+
* Deletes some option rows of a product component, leaving the group and its other rows.
|
|
1271
|
+
* The rows stay in reads with `deleteDate` set. Throws without calling the API when
|
|
1272
|
+
* `instanceIds` is empty, because a delete without them would delete the whole group.
|
|
1273
|
+
*/
|
|
1274
|
+
async function deleteProductComponentInstances(id, instanceIds, options = {}) {
|
|
1275
|
+
const def = SHOPWAVE_ENTITIES.productComponent;
|
|
1276
|
+
const groupId = id == null ? "" : String(id).trim();
|
|
1277
|
+
if (!groupId) throw new Error("productComponent id is missing");
|
|
1278
|
+
const ids = (instanceIds ?? []).map((v) => String(v).trim()).filter(Boolean);
|
|
1279
|
+
if (ids.length === 0) throw new Error("productComponent instance ids are missing");
|
|
1280
|
+
await apiDelete(`/api/${def.route}/${encodeURIComponent(groupId)}`, options, { [def.childIdsHeader]: ids.join(",") });
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
//#endregion
|
|
1284
|
+
//#region src/core/promotion.ts
|
|
1285
|
+
/** Promotion rule identifiers as stored by the Shopwave API. */
|
|
1286
|
+
let PromotionRuleSet = /* @__PURE__ */ function(PromotionRuleSet$1) {
|
|
1287
|
+
PromotionRuleSet$1["Percentage"] = "|%^/";
|
|
1288
|
+
PromotionRuleSet$1["Combo"] = "&%</";
|
|
1289
|
+
PromotionRuleSet$1["MealDeal"] = "&=^/";
|
|
1290
|
+
return PromotionRuleSet$1;
|
|
1291
|
+
}({});
|
|
1292
|
+
/**
|
|
1293
|
+
* Fetches promotions. Returns the flattened `Promotion[]`.
|
|
1294
|
+
*/
|
|
1295
|
+
async function fetchPromotions(params = {}, options = {}) {
|
|
1296
|
+
const extras = { deleted: params.deleted ?? false };
|
|
1297
|
+
if (params.promotionIds) extras.promotionIds = params.promotionIds;
|
|
1298
|
+
if (params.imminent !== void 0) extras.imminent = params.imminent;
|
|
1299
|
+
if (params.active !== void 0) extras.active = params.active;
|
|
1300
|
+
if (params.expired !== void 0) extras.expired = params.expired;
|
|
1301
|
+
const json = await apiGet("/api/promotions", extras, withToken(options, params.token));
|
|
1302
|
+
assertNoApiErrors(json, 200);
|
|
1303
|
+
return Object.values(json?.promotions ?? {});
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
//#endregion
|
|
1307
|
+
//#region src/core/store.ts
|
|
1308
|
+
/**
|
|
1309
|
+
* Fetches stores. Returns the flattened `Store[]`.
|
|
1310
|
+
*/
|
|
1311
|
+
async function fetchStores(params = {}, options = {}) {
|
|
1312
|
+
const extras = { deleted: params.deleted ?? false };
|
|
1313
|
+
if (params.storeIds) extras.storeIds = params.storeIds;
|
|
1314
|
+
const json = await apiGet("/api/stores", extras, withToken(options, params.token));
|
|
1315
|
+
assertNoApiErrors(json, 200);
|
|
1316
|
+
return Object.values(json?.stores ?? {});
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
//#endregion
|
|
1320
|
+
//#region src/core/report.ts
|
|
1321
|
+
/**
|
|
1322
|
+
* Runs one or more report queries and returns the `reports` map keyed by query
|
|
1323
|
+
* name. The query is JSON-encoded into the `extras.query` header, matching the
|
|
1324
|
+
* original endpoint contract.
|
|
1325
|
+
*/
|
|
1326
|
+
async function fetchReport(query, options = {}) {
|
|
1327
|
+
const json = await apiGet("/api/report", { query: JSON.stringify(query) }, options);
|
|
1328
|
+
assertNoApiErrors(json, 200);
|
|
1329
|
+
return json?.reports ?? {};
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
//#endregion
|
|
1333
|
+
//#region src/core/session.ts
|
|
1334
|
+
/**
|
|
1335
|
+
* Reads the current login status from `GET /api/session`.
|
|
1336
|
+
*
|
|
1337
|
+
* Works with the new `react-shopwave-connect/next` session route
|
|
1338
|
+
* (`{ loggedIn }`) and with older routes that returned the raw session
|
|
1339
|
+
* (`{ token: {...} }`) — in that case only a boolean is surfaced.
|
|
1340
|
+
*/
|
|
1341
|
+
async function fetchSession(options = {}) {
|
|
1342
|
+
const doFetch = options.fetch ?? (typeof fetch !== "undefined" ? fetch : void 0);
|
|
1343
|
+
if (!doFetch) throw new Error("No fetch implementation available. Pass `options.fetch`.");
|
|
1344
|
+
const response = await doFetch(`${options.baseUrl ?? ""}/api/session`, {
|
|
1345
|
+
method: "GET",
|
|
1346
|
+
headers: { Accept: "application/json" },
|
|
1347
|
+
cache: "no-store",
|
|
1348
|
+
credentials: "same-origin",
|
|
1349
|
+
signal: options.signal
|
|
1350
|
+
});
|
|
1351
|
+
if (response.status === 401) return { loggedIn: false };
|
|
1352
|
+
if (!response.ok) throw new Error(`Session request failed (${response.status})`);
|
|
1353
|
+
const json = await response.json();
|
|
1354
|
+
if (json && typeof json.loggedIn === "boolean") return {
|
|
1355
|
+
loggedIn: json.loggedIn,
|
|
1356
|
+
expiresAt: typeof json.expiresAt === "number" ? json.expiresAt : void 0
|
|
1357
|
+
};
|
|
1358
|
+
return { loggedIn: Boolean(json && json.token) };
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Path that starts a login and returns to `returnTo` afterwards.
|
|
1362
|
+
* Navigate the browser to it (`window.location.href = loginPath(...)`).
|
|
1363
|
+
*/
|
|
1364
|
+
function loginPath(returnTo, authPath = "/auth") {
|
|
1365
|
+
return returnTo ? `${authPath}?returnTo=${encodeURIComponent(returnTo)}` : authPath;
|
|
1366
|
+
}
|
|
1367
|
+
/** Path that logs out of the app and the Shopwave auth server. */
|
|
1368
|
+
function logoutPath(authPath = "/auth") {
|
|
1369
|
+
return `${authPath}/logout`;
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Ends the current server session (no redirect).
|
|
1373
|
+
*
|
|
1374
|
+
* Prefer navigating to {@link logoutPath} when using
|
|
1375
|
+
* `react-shopwave-connect/next`: it also signs the user out of the auth server.
|
|
1376
|
+
*/
|
|
1377
|
+
async function logout(options = {}) {
|
|
1378
|
+
await apiDelete("/api/session?action=logout", options);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/core/entity.ts
|
|
1383
|
+
/**
|
|
1384
|
+
* Low-level, untyped mutations: the caller supplies the route and payload.
|
|
1385
|
+
*
|
|
1386
|
+
* Prefer the typed functions in `./entities` (`saveProduct`, `deleteCategory`,
|
|
1387
|
+
* …): they know each route, build the request envelope, check the response and
|
|
1388
|
+
* return the saved entity with its id. Keep these for routes that have no typed
|
|
1389
|
+
* function yet (e.g. `/api/merchant`).
|
|
1390
|
+
*/
|
|
1391
|
+
/**
|
|
1392
|
+
* Deletes `/api/{resourcePath}/{entityId}`.
|
|
1393
|
+
*/
|
|
1394
|
+
async function deleteEntity(resourcePath, entityId, options = {}) {
|
|
1395
|
+
if (entityId == null || entityId === "") throw new Error("entityId is missing");
|
|
1396
|
+
await apiDelete(`/api/${resourcePath}/${entityId}`, options);
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Sends a create/update mutation. Returns the response's `result` field when it
|
|
1400
|
+
* has one (older app routes wrapped their answer that way), otherwise the whole
|
|
1401
|
+
* parsed response body. Throws `ShopwaveApiError` on HTTP or API errors.
|
|
1402
|
+
*/
|
|
1403
|
+
async function submitEntity({ endpoint, method = "POST", payload }, options = {}) {
|
|
1404
|
+
const json = await apiSend(endpoint, method, payload, options);
|
|
1405
|
+
assertNoApiErrors(json, 200);
|
|
1406
|
+
if (json && typeof json === "object" && "result" in json) return json.result;
|
|
1407
|
+
return json ?? void 0;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1272
1410
|
//#endregion
|
|
1273
1411
|
//#region src/core/basket.ts
|
|
1274
1412
|
const DEFAULT_BASKET_FILTERS = {
|
|
@@ -1442,9 +1580,9 @@ function trimmed(value) {
|
|
|
1442
1580
|
/**
|
|
1443
1581
|
* Reads `merchant.links` as flat fields (`{ website, twitter, facebook,
|
|
1444
1582
|
* instagram }`), whatever shape it was saved in:
|
|
1445
|
-
* - the nested shape
|
|
1583
|
+
* - the nested shape the existing admin saves (`{ website: { home }, social: { … } }`);
|
|
1446
1584
|
* - flat keys (`{ website: "…" }`, what SDK 0.4.0 pre-releases sent);
|
|
1447
|
-
* - an array in {@link MERCHANT_LINK_KEYS} order (older
|
|
1585
|
+
* - an array in {@link MERCHANT_LINK_KEYS} order (older app versions).
|
|
1448
1586
|
* Empty values are dropped.
|
|
1449
1587
|
*/
|
|
1450
1588
|
function merchantLinksToForm(links) {
|
|
@@ -1481,7 +1619,7 @@ function merchantLinksFromForm(form) {
|
|
|
1481
1619
|
}
|
|
1482
1620
|
return out;
|
|
1483
1621
|
}
|
|
1484
|
-
/** Reads any stored or older `links` value into the nested shape
|
|
1622
|
+
/** Reads any stored or older `links` value into the nested shape the existing admin saves. */
|
|
1485
1623
|
function normalizeMerchantLinks(links) {
|
|
1486
1624
|
return merchantLinksFromForm(merchantLinksToForm(links));
|
|
1487
1625
|
}
|
|
@@ -1845,6 +1983,25 @@ function useProductInstanceTags(options) {
|
|
|
1845
1983
|
}), [options?.baseUrl, options?.token], options?.enabled ?? true);
|
|
1846
1984
|
}
|
|
1847
1985
|
|
|
1986
|
+
//#endregion
|
|
1987
|
+
//#region src/hooks/useProductComponent.ts
|
|
1988
|
+
/**
|
|
1989
|
+
* Auto-fetches product components (modifier groups) on mount and whenever the params change:
|
|
1990
|
+
* a product's groups with `{ productIds: [id] }`, one group with `{ productComponentIds: [id] }`,
|
|
1991
|
+
* or every group with no filter. Option rows with a `deleteDate` are included; skip them when
|
|
1992
|
+
* listing options.
|
|
1993
|
+
*/
|
|
1994
|
+
function useProductComponent(params = {}, options) {
|
|
1995
|
+
return useQuery((signal) => fetchProductComponents(params, {
|
|
1996
|
+
...options,
|
|
1997
|
+
signal
|
|
1998
|
+
}), [
|
|
1999
|
+
JSON.stringify(params),
|
|
2000
|
+
options?.baseUrl,
|
|
2001
|
+
options?.token
|
|
2002
|
+
]);
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1848
2005
|
//#endregion
|
|
1849
2006
|
//#region src/hooks/usePromotion.ts
|
|
1850
2007
|
/**
|
|
@@ -2155,5 +2312,5 @@ function useBasketReport(options) {
|
|
|
2155
2312
|
}
|
|
2156
2313
|
|
|
2157
2314
|
//#endregion
|
|
2158
|
-
export { DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, ImageRuleError, ImageTooSmallError, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, PromotionRuleSet, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, ShopwaveApiError, UPLOAD_KINDS, UPLOAD_PREFIXES, apiDelete, apiGet, apiRequest, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, checkImageRule, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, isUploadPrefix, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseAspectRatio, parseBasketReportData, planImageFit, prepareImage, productImageRole, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, savePromotion, saveStore, setMerchantImage, submitEntity, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, toStoredProductImages, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
|
|
2315
|
+
export { DEFAULT_BASKET_FILTERS, DEFAULT_PAGINATION, ImageRuleError, ImageTooSmallError, MERCHANT_COLOUR_KEYS, MERCHANT_FIELD_MAX_LENGTH, MERCHANT_IMAGE_SLOTS, MERCHANT_IMAGE_SLOT_KEYS, MERCHANT_LINK_KEYS, MERCHANT_LINK_PATHS, MERCHANT_OBJECT_FIELDS, MERCHANT_READ_ONLY_FIELDS, PromotionRuleSet, SHOPWAVE_ENTITIES, SHOPWAVE_IMAGE_BASE_URL, SHOPWAVE_LOOKUPS, SHOPWAVE_RESOURCES, SHOPWAVE_UPLOAD, ShopwaveApiError, UPLOAD_KINDS, UPLOAD_PREFIXES, apiDelete, apiGet, apiRequest, apiSend, assertNoApiErrors, authorizationFor, buildBasketReportQuery, buildConsumerMap, checkImageRule, collectConsumerIds, collectProductIds, combineBasketRows, computeBasketSummary, coverCrop, deleteCategory, deleteEmployee, deleteEntity, deleteEntityById, deleteProduct, deleteProductComponent, deleteProductComponentInstances, deletePromotion, deleteStore, fetchCategories, fetchCategory, fetchConsumer, fetchConsumers, fetchEmployee, fetchEmployees, fetchEntityById, fetchMerchant, fetchProduct, fetchProductComponent, fetchProductComponents, fetchProductInstanceTags, fetchProductTags, fetchProducts, fetchProductsMap, fetchPromotion, fetchPromotions, fetchReport, fetchSession, fetchStore, fetchStores, fetchUser, findRepricedInstances, fitImageToSize, formatDateForQuery, getApiErrorMap, getApiErrors, getImageUrl, getMerchantImage, getMerchantImageUrl, imageFileName, isSameInstancePrice, isUploadKind, isUploadPrefix, loginPath, logout, logoutPath, merchantLinksFromForm, merchantLinksToForm, normalizeMerchantLinks, normalizeTagList, parseAspectRatio, parseBasketReportData, planImageFit, prepareImage, productImageRole, readImageSize, rolesById, saveCategory, saveEmployee, saveEntities, saveEntity, saveProduct, saveProductComponent, savePromotion, saveStore, setMerchantImage, submitEntity, toProductComponentForSave, toProductInstancesForSave, toShopwaveApiError, toStoredImageIds, toStoredProductImages, updateMerchant, uploadImage, uploadMerchantImage, useBasketReport, useCategory, useConsumer, useDelete, useEmployee, useLogout, useMerchant, useProduct, useProductComponent, useProductInstanceTags, useProductTags, usePromotion, useReport, useSession, useStore, useSubmit, useUpdateMerchant, useUploadImage, useUploadMerchantImage, useUser, validateMerchant, withToken };
|
|
2159
2316
|
//# sourceMappingURL=index.js.map
|