react-shopwave-connect 0.4.2 → 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.
@@ -283,6 +283,106 @@ async function fetchProductsMap(productIds, options = {}, batchSize = 200) {
283
283
  return result;
284
284
  }
285
285
 
286
+ //#endregion
287
+ //#region src/core/resources.ts
288
+ /**
289
+ * - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
290
+ * updates it (without an `id` the API would create a second merchant, so the
291
+ * route requires one).
292
+ * - user: `GET /user` (read-only here; the API's `POST /user` invites users and
293
+ * needs the admin scope).
294
+ */
295
+ const SHOPWAVE_RESOURCES = Object.freeze({
296
+ merchant: {
297
+ route: "merchant",
298
+ key: "merchant",
299
+ upstream: "merchant",
300
+ writable: true
301
+ },
302
+ user: {
303
+ route: "user",
304
+ key: "user",
305
+ upstream: "user",
306
+ writable: false
307
+ }
308
+ });
309
+ /** `UploadKind` → the API's `contentType` value. */
310
+ const UPLOAD_KINDS = Object.freeze({
311
+ merchant: "merchant",
312
+ merchantFeature: "merchantFeature",
313
+ product: "product",
314
+ user: "user",
315
+ applicationLogo: "applicaionLogo",
316
+ applicationImages: "applicaionImages"
317
+ });
318
+ /** How uploads are addressed (app route and Shopwave uploader). */
319
+ const SHOPWAVE_UPLOAD = Object.freeze({
320
+ route: "upload",
321
+ upstream: "uploader",
322
+ kindHeader: "contentType",
323
+ fileField: "file",
324
+ kindField: "kind"
325
+ });
326
+ function isUploadKind(value) {
327
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
328
+ }
329
+ const SHOPWAVE_LOOKUPS = Object.freeze({
330
+ productTags: {
331
+ route: "products/tags",
332
+ key: "tags",
333
+ upstream: "product/tags"
334
+ },
335
+ productInstanceTags: {
336
+ route: "products/instance-tags",
337
+ key: "tags",
338
+ upstream: "product/instance/tags"
339
+ }
340
+ });
341
+
342
+ //#endregion
343
+ //#region src/core/productTags.ts
344
+ /**
345
+ * Turns a tag list from the API into clean strings: trims each tag, drops
346
+ * blanks and anything that isn't text, and removes exact duplicates (first one
347
+ * wins). The API's own order is kept. A map is accepted too (its values are
348
+ * used), in case the API ever answers keyed by id.
349
+ */
350
+ function normalizeTagList(value) {
351
+ const list = Array.isArray(value) ? value : value && typeof value === "object" ? Object.values(value) : [];
352
+ const seen = /* @__PURE__ */ new Set();
353
+ const out = [];
354
+ for (const item of list) {
355
+ if (typeof item !== "string" && typeof item !== "number") continue;
356
+ const tag = String(item).trim();
357
+ if (!tag || seen.has(tag)) continue;
358
+ seen.add(tag);
359
+ out.push(tag);
360
+ }
361
+ return out;
362
+ }
363
+ async function fetchLookup(kind, options) {
364
+ const def = SHOPWAVE_LOOKUPS[kind];
365
+ if (!def) throw new Error(`Unknown Shopwave lookup "${kind}"`);
366
+ const json = await apiGet(`/api/${def.route}`, {}, options);
367
+ assertNoApiErrors(json, 200);
368
+ return normalizeTagList(json?.[def.key]);
369
+ }
370
+ /**
371
+ * Every distinct tag used on the merchant's products (`GET /product/tags`),
372
+ * for a tag field's suggestions. Returns `[]` when there are none.
373
+ */
374
+ function fetchProductTags(options = {}) {
375
+ return fetchLookup("productTags", options);
376
+ }
377
+ /**
378
+ * Every distinct tag used on the merchant's price instances
379
+ * (`GET /product/instance/tags`): the channel names (Eat In, Takeout, …).
380
+ * Returns `[]` when there are none.
381
+ */
382
+ function fetchProductInstanceTags(options = {}) {
383
+ return fetchLookup("productInstanceTags", options);
384
+ }
385
+
286
386
  //#endregion
287
387
  //#region src/core/promotion.ts
288
388
  /**
@@ -536,50 +636,6 @@ function buildConsumerMap(consumers) {
536
636
  return map;
537
637
  }
538
638
 
539
- //#endregion
540
- //#region src/core/resources.ts
541
- /**
542
- * - merchant: `GET /merchant`; `POST /merchant` with `{ merchant: { id, … } }`
543
- * updates it (without an `id` the API would create a second merchant, so the
544
- * route requires one).
545
- * - user: `GET /user` (read-only here; the API's `POST /user` invites users and
546
- * needs the admin scope).
547
- */
548
- const SHOPWAVE_RESOURCES = Object.freeze({
549
- merchant: {
550
- route: "merchant",
551
- key: "merchant",
552
- upstream: "merchant",
553
- writable: true
554
- },
555
- user: {
556
- route: "user",
557
- key: "user",
558
- upstream: "user",
559
- writable: false
560
- }
561
- });
562
- /** `UploadKind` → the API's `contentType` value. */
563
- const UPLOAD_KINDS = Object.freeze({
564
- merchant: "merchant",
565
- merchantFeature: "merchantFeature",
566
- product: "product",
567
- user: "user",
568
- applicationLogo: "applicaionLogo",
569
- applicationImages: "applicaionImages"
570
- });
571
- /** How uploads are addressed (app route and Shopwave uploader). */
572
- const SHOPWAVE_UPLOAD = Object.freeze({
573
- route: "upload",
574
- upstream: "uploader",
575
- kindHeader: "contentType",
576
- fileField: "file",
577
- kindField: "kind"
578
- });
579
- function isUploadKind(value) {
580
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
581
- }
582
-
583
639
  //#endregion
584
640
  //#region src/core/imageFit.ts
585
641
  /**
@@ -1124,6 +1180,33 @@ function useProduct(params = {}, options) {
1124
1180
  ]);
1125
1181
  }
1126
1182
 
1183
+ //#endregion
1184
+ //#region src/hooks/useProductTags.ts
1185
+ /**
1186
+ * The merchant's distinct product tags, for a tag field's suggestions.
1187
+ * `data` is `string[]` once loaded.
1188
+ *
1189
+ * @example
1190
+ * const [opened, setOpened] = useState(false);
1191
+ * const { data: tags, loading } = useProductTags({ enabled: opened });
1192
+ */
1193
+ function useProductTags(options) {
1194
+ return useQuery((signal) => fetchProductTags({
1195
+ ...options,
1196
+ signal
1197
+ }), [options?.baseUrl, options?.token], options?.enabled ?? true);
1198
+ }
1199
+ /**
1200
+ * The merchant's distinct price-instance (channel) tags: Eat In, Takeout, …
1201
+ * `data` is `string[]` once loaded.
1202
+ */
1203
+ function useProductInstanceTags(options) {
1204
+ return useQuery((signal) => fetchProductInstanceTags({
1205
+ ...options,
1206
+ signal
1207
+ }), [options?.baseUrl, options?.token], options?.enabled ?? true);
1208
+ }
1209
+
1127
1210
  //#endregion
1128
1211
  //#region src/hooks/usePromotion.ts
1129
1212
  /**
@@ -1442,6 +1525,8 @@ exports.useEmployee = useEmployee;
1442
1525
  exports.useLogout = useLogout;
1443
1526
  exports.useMerchant = useMerchant;
1444
1527
  exports.useProduct = useProduct;
1528
+ exports.useProductInstanceTags = useProductInstanceTags;
1529
+ exports.useProductTags = useProductTags;
1445
1530
  exports.usePromotion = usePromotion;
1446
1531
  exports.useReport = useReport;
1447
1532
  exports.useSession = useSession;