react-shopwave-connect 0.4.3 → 0.4.4
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 +6 -4
- package/dist/core/index.cjs +484 -424
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +56 -2
- package/dist/core/index.d.ts +56 -2
- package/dist/core/index.js +481 -425
- package/dist/core/index.js.map +1 -1
- package/dist/hooks/index.cjs +159 -145
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +19 -0
- package/dist/hooks/index.d.ts +19 -0
- package/dist/hooks/index.js +159 -145
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +484 -424
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -2
- package/dist/index.d.ts +56 -2
- package/dist/index.js +481 -425
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +75 -57
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +21 -4
- package/dist/next/index.d.ts +21 -4
- package/dist/next/index.js +75 -58
- package/dist/next/index.js.map +1 -1
- package/dist/server/index.cjs +75 -57
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +26 -5
- package/dist/server/index.d.ts +26 -5
- package/dist/server/index.js +75 -58
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/hooks/index.cjs
CHANGED
|
@@ -315,17 +315,29 @@ const UPLOAD_KINDS = Object.freeze({
|
|
|
315
315
|
applicationLogo: "applicaionLogo",
|
|
316
316
|
applicationImages: "applicaionImages"
|
|
317
317
|
});
|
|
318
|
+
/** Every `UploadPrefix`, longest first so a name check never takes `product` for `product_thumbnail`. */
|
|
319
|
+
const UPLOAD_PREFIXES = Object.freeze([
|
|
320
|
+
"product_thumbnail",
|
|
321
|
+
"variant_thumbnail",
|
|
322
|
+
"product",
|
|
323
|
+
"variant"
|
|
324
|
+
]);
|
|
318
325
|
/** How uploads are addressed (app route and Shopwave uploader). */
|
|
319
326
|
const SHOPWAVE_UPLOAD = Object.freeze({
|
|
320
327
|
route: "upload",
|
|
321
328
|
upstream: "uploader",
|
|
322
329
|
kindHeader: "contentType",
|
|
330
|
+
prefixHeader: "prefix",
|
|
323
331
|
fileField: "file",
|
|
324
|
-
kindField: "kind"
|
|
332
|
+
kindField: "kind",
|
|
333
|
+
prefixField: "prefix"
|
|
325
334
|
});
|
|
326
335
|
function isUploadKind(value) {
|
|
327
336
|
return typeof value === "string" && Object.prototype.hasOwnProperty.call(UPLOAD_KINDS, value);
|
|
328
337
|
}
|
|
338
|
+
function isUploadPrefix(value) {
|
|
339
|
+
return typeof value === "string" && UPLOAD_PREFIXES.includes(value);
|
|
340
|
+
}
|
|
329
341
|
const SHOPWAVE_LOOKUPS = Object.freeze({
|
|
330
342
|
productTags: {
|
|
331
343
|
route: "products/tags",
|
|
@@ -492,150 +504,6 @@ async function submitEntity({ endpoint, method = "POST", payload }, options = {}
|
|
|
492
504
|
return json ?? void 0;
|
|
493
505
|
}
|
|
494
506
|
|
|
495
|
-
//#endregion
|
|
496
|
-
//#region src/core/basket.ts
|
|
497
|
-
const DEFAULT_BASKET_FILTERS = {
|
|
498
|
-
search: "",
|
|
499
|
-
dateRange: {
|
|
500
|
-
start: null,
|
|
501
|
-
end: null
|
|
502
|
-
},
|
|
503
|
-
consumerId: null,
|
|
504
|
-
status: "all"
|
|
505
|
-
};
|
|
506
|
-
const DEFAULT_PAGINATION = {
|
|
507
|
-
page: 0,
|
|
508
|
-
pageSize: 25,
|
|
509
|
-
total: 0
|
|
510
|
-
};
|
|
511
|
-
/** Formats a date as `YYYY-MM-DD HH:mm:ss` for the report query. */
|
|
512
|
-
function formatDateForQuery(date, isEndOfDay = false) {
|
|
513
|
-
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")} ${isEndOfDay ? "23:59:59" : "00:00:00"}`;
|
|
514
|
-
}
|
|
515
|
-
/** Builds the `Basket` report query from a set of filters. */
|
|
516
|
-
function buildBasketReportQuery(filters) {
|
|
517
|
-
const whereConditions = [];
|
|
518
|
-
const today = /* @__PURE__ */ new Date();
|
|
519
|
-
const startDate = filters.dateRange.start ?? today;
|
|
520
|
-
const endDate = filters.dateRange.end ?? today;
|
|
521
|
-
whereConditions.push(`Basket.completeDate > '${formatDateForQuery(startDate, false)}'`);
|
|
522
|
-
whereConditions.push(`Basket.completeDate < '${formatDateForQuery(endDate, true)}'`);
|
|
523
|
-
if (filters.consumerId) whereConditions.push(`Basket.consumerId = ${filters.consumerId}`);
|
|
524
|
-
if (filters.status !== "all") whereConditions.push(`Basket.status = '${filters.status}'`);
|
|
525
|
-
return { baskets: {
|
|
526
|
-
FROM: "Basket",
|
|
527
|
-
WHERE: whereConditions.length > 0 ? { AND: whereConditions } : void 0
|
|
528
|
-
} };
|
|
529
|
-
}
|
|
530
|
-
/** Flattens the raw report rows/keys into structured baskets. */
|
|
531
|
-
function parseBasketReportData(reports) {
|
|
532
|
-
if (!reports?.baskets?.data || !reports?.baskets?.keys) return [];
|
|
533
|
-
const { keys, data } = reports.baskets;
|
|
534
|
-
const keyIndices = {};
|
|
535
|
-
keys.forEach((key, index) => {
|
|
536
|
-
keyIndices[key.name] = index;
|
|
537
|
-
});
|
|
538
|
-
const productKey = keys.find((k) => k.name === "Basket.Product");
|
|
539
|
-
const productSubKeys = {};
|
|
540
|
-
if (productKey?.item) productKey.item.forEach((subKey, index) => {
|
|
541
|
-
productSubKeys[subKey.name] = index;
|
|
542
|
-
});
|
|
543
|
-
return data.map((row) => {
|
|
544
|
-
const products = (row[keyIndices["Basket.Product"]] || []).map((productRow) => ({
|
|
545
|
-
productId: productRow[productSubKeys["Basket.Product.productId"]],
|
|
546
|
-
productName: `Product #${productRow[productSubKeys["Basket.Product.productId"]]}`,
|
|
547
|
-
quantity: productRow[productSubKeys["Basket.Product.quantity"]] || 1,
|
|
548
|
-
price: productRow[productSubKeys["Basket.Product.productPrice"]] || 0,
|
|
549
|
-
tax: productRow[productSubKeys["Basket.Product.productTaxPercentage"]] || 0
|
|
550
|
-
}));
|
|
551
|
-
const total = products.reduce((sum, p) => sum + p.price * p.quantity, 0);
|
|
552
|
-
const taxTotal = products.reduce((sum, p) => sum + p.price * p.quantity * p.tax, 0);
|
|
553
|
-
const completeDateTimestamp = row[keyIndices["Basket.completeDate"]];
|
|
554
|
-
const completeDate = completeDateTimestamp ? new Date(completeDateTimestamp).toISOString() : "";
|
|
555
|
-
return {
|
|
556
|
-
id: row[keyIndices["Basket.id"]],
|
|
557
|
-
name: row[keyIndices["Basket.name"]],
|
|
558
|
-
consumerId: row[keyIndices["Basket.consumerId"]],
|
|
559
|
-
products,
|
|
560
|
-
total,
|
|
561
|
-
taxTotal,
|
|
562
|
-
status: "completed",
|
|
563
|
-
completeDate
|
|
564
|
-
};
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
/** Unique non-null consumer ids referenced by a set of baskets. */
|
|
568
|
-
function collectConsumerIds(baskets) {
|
|
569
|
-
if (!baskets.length) return null;
|
|
570
|
-
const ids = baskets.map((b) => b.consumerId).filter((id) => id != null);
|
|
571
|
-
return ids.length > 0 ? [...new Set(ids)] : null;
|
|
572
|
-
}
|
|
573
|
-
/** Unique product ids referenced by a set of baskets. */
|
|
574
|
-
function collectProductIds(baskets) {
|
|
575
|
-
if (!baskets.length) return null;
|
|
576
|
-
const ids = /* @__PURE__ */ new Set();
|
|
577
|
-
baskets.forEach((basket) => {
|
|
578
|
-
basket.products.forEach((product) => {
|
|
579
|
-
if (product.productId) ids.add(product.productId);
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
return ids.size > 0 ? [...ids] : null;
|
|
583
|
-
}
|
|
584
|
-
/** Joins baskets with consumer + product lookups, applies search, sorts. */
|
|
585
|
-
function combineBasketRows({ baskets, consumerMap, productMap, search }) {
|
|
586
|
-
if (!baskets.length) return [];
|
|
587
|
-
let combinedRows = baskets.map((basket) => {
|
|
588
|
-
const consumer = basket.consumerId ? consumerMap[basket.consumerId] : null;
|
|
589
|
-
const productsWithNames = basket.products.map((p) => {
|
|
590
|
-
const product = productMap[p.productId];
|
|
591
|
-
return {
|
|
592
|
-
...p,
|
|
593
|
-
productName: product?.name || `Product #${p.productId}`
|
|
594
|
-
};
|
|
595
|
-
});
|
|
596
|
-
return {
|
|
597
|
-
id: basket.id,
|
|
598
|
-
basketId: basket.id,
|
|
599
|
-
basketName: basket.name || `Basket #${basket.id}`,
|
|
600
|
-
consumerId: basket.consumerId,
|
|
601
|
-
consumerName: consumer ? `${consumer.firstName} ${consumer.lastName}` : "Unknown",
|
|
602
|
-
consumerEmail: consumer?.email || "-",
|
|
603
|
-
products: productsWithNames,
|
|
604
|
-
basketTotal: (basket.total || 0) / 100,
|
|
605
|
-
tax: basket.taxTotal || 0,
|
|
606
|
-
dateTime: basket.completeDate,
|
|
607
|
-
status: basket.status || "pending"
|
|
608
|
-
};
|
|
609
|
-
});
|
|
610
|
-
if (search) {
|
|
611
|
-
const searchLower = search.toLowerCase();
|
|
612
|
-
combinedRows = combinedRows.filter((row) => row.basketId.toString().includes(searchLower) || row.basketName.toLowerCase().includes(searchLower) || row.consumerName.toLowerCase().includes(searchLower) || row.consumerEmail.toLowerCase().includes(searchLower));
|
|
613
|
-
}
|
|
614
|
-
combinedRows.sort((a, b) => {
|
|
615
|
-
const dateA = new Date(a.dateTime).getTime();
|
|
616
|
-
return new Date(b.dateTime).getTime() - dateA;
|
|
617
|
-
});
|
|
618
|
-
return combinedRows;
|
|
619
|
-
}
|
|
620
|
-
/** Aggregate totals for a set of report rows. */
|
|
621
|
-
function computeBasketSummary(rows) {
|
|
622
|
-
const uniqueConsumerIds = new Set(rows.map((row) => row.consumerId));
|
|
623
|
-
return {
|
|
624
|
-
totalBaskets: rows.length,
|
|
625
|
-
totalRevenue: rows.reduce((sum, row) => sum + row.basketTotal, 0),
|
|
626
|
-
totalTax: rows.reduce((sum, row) => sum + row.tax, 0),
|
|
627
|
-
uniqueConsumers: uniqueConsumerIds.size
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
/** Builds a consumer id → consumer lookup. */
|
|
631
|
-
function buildConsumerMap(consumers) {
|
|
632
|
-
const map = {};
|
|
633
|
-
if (consumers) consumers.forEach((consumer) => {
|
|
634
|
-
map[consumer.id] = consumer;
|
|
635
|
-
});
|
|
636
|
-
return map;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
507
|
//#endregion
|
|
640
508
|
//#region src/core/imageFit.ts
|
|
641
509
|
/**
|
|
@@ -746,10 +614,12 @@ function getImageUrl(path, base = SHOPWAVE_IMAGE_BASE_URL) {
|
|
|
746
614
|
async function uploadImage(file, params, options = {}) {
|
|
747
615
|
if (!file || typeof file.size !== "number") throw new Error("uploadImage: file must be a File or Blob");
|
|
748
616
|
if (!isUploadKind(params?.kind)) throw new Error(`uploadImage: unknown kind "${params?.kind}"`);
|
|
617
|
+
if (params.prefix != null && !isUploadPrefix(params.prefix)) throw new Error(`uploadImage: unknown prefix "${params.prefix}"`);
|
|
749
618
|
const name = params.fileName ?? file.name ?? "upload";
|
|
750
619
|
const form = new FormData();
|
|
751
620
|
form.append(SHOPWAVE_UPLOAD.fileField, file, name);
|
|
752
621
|
form.append(SHOPWAVE_UPLOAD.kindField, params.kind);
|
|
622
|
+
if (params.prefix != null) form.append(SHOPWAVE_UPLOAD.prefixField, params.prefix);
|
|
753
623
|
const { status, body: raw } = await apiRequest(`/api/${SHOPWAVE_UPLOAD.route}`, {
|
|
754
624
|
method: "POST",
|
|
755
625
|
body: form
|
|
@@ -837,6 +707,150 @@ async function uploadMerchantImage(file, slot, options = {}) {
|
|
|
837
707
|
return uploadImage(fit ? await fitImageToSize(file, def.width, def.height, fitOptions) : file, { kind: def.uploadKind }, requestOptions);
|
|
838
708
|
}
|
|
839
709
|
|
|
710
|
+
//#endregion
|
|
711
|
+
//#region src/core/basket.ts
|
|
712
|
+
const DEFAULT_BASKET_FILTERS = {
|
|
713
|
+
search: "",
|
|
714
|
+
dateRange: {
|
|
715
|
+
start: null,
|
|
716
|
+
end: null
|
|
717
|
+
},
|
|
718
|
+
consumerId: null,
|
|
719
|
+
status: "all"
|
|
720
|
+
};
|
|
721
|
+
const DEFAULT_PAGINATION = {
|
|
722
|
+
page: 0,
|
|
723
|
+
pageSize: 25,
|
|
724
|
+
total: 0
|
|
725
|
+
};
|
|
726
|
+
/** Formats a date as `YYYY-MM-DD HH:mm:ss` for the report query. */
|
|
727
|
+
function formatDateForQuery(date, isEndOfDay = false) {
|
|
728
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")} ${isEndOfDay ? "23:59:59" : "00:00:00"}`;
|
|
729
|
+
}
|
|
730
|
+
/** Builds the `Basket` report query from a set of filters. */
|
|
731
|
+
function buildBasketReportQuery(filters) {
|
|
732
|
+
const whereConditions = [];
|
|
733
|
+
const today = /* @__PURE__ */ new Date();
|
|
734
|
+
const startDate = filters.dateRange.start ?? today;
|
|
735
|
+
const endDate = filters.dateRange.end ?? today;
|
|
736
|
+
whereConditions.push(`Basket.completeDate > '${formatDateForQuery(startDate, false)}'`);
|
|
737
|
+
whereConditions.push(`Basket.completeDate < '${formatDateForQuery(endDate, true)}'`);
|
|
738
|
+
if (filters.consumerId) whereConditions.push(`Basket.consumerId = ${filters.consumerId}`);
|
|
739
|
+
if (filters.status !== "all") whereConditions.push(`Basket.status = '${filters.status}'`);
|
|
740
|
+
return { baskets: {
|
|
741
|
+
FROM: "Basket",
|
|
742
|
+
WHERE: whereConditions.length > 0 ? { AND: whereConditions } : void 0
|
|
743
|
+
} };
|
|
744
|
+
}
|
|
745
|
+
/** Flattens the raw report rows/keys into structured baskets. */
|
|
746
|
+
function parseBasketReportData(reports) {
|
|
747
|
+
if (!reports?.baskets?.data || !reports?.baskets?.keys) return [];
|
|
748
|
+
const { keys, data } = reports.baskets;
|
|
749
|
+
const keyIndices = {};
|
|
750
|
+
keys.forEach((key, index) => {
|
|
751
|
+
keyIndices[key.name] = index;
|
|
752
|
+
});
|
|
753
|
+
const productKey = keys.find((k) => k.name === "Basket.Product");
|
|
754
|
+
const productSubKeys = {};
|
|
755
|
+
if (productKey?.item) productKey.item.forEach((subKey, index) => {
|
|
756
|
+
productSubKeys[subKey.name] = index;
|
|
757
|
+
});
|
|
758
|
+
return data.map((row) => {
|
|
759
|
+
const products = (row[keyIndices["Basket.Product"]] || []).map((productRow) => ({
|
|
760
|
+
productId: productRow[productSubKeys["Basket.Product.productId"]],
|
|
761
|
+
productName: `Product #${productRow[productSubKeys["Basket.Product.productId"]]}`,
|
|
762
|
+
quantity: productRow[productSubKeys["Basket.Product.quantity"]] || 1,
|
|
763
|
+
price: productRow[productSubKeys["Basket.Product.productPrice"]] || 0,
|
|
764
|
+
tax: productRow[productSubKeys["Basket.Product.productTaxPercentage"]] || 0
|
|
765
|
+
}));
|
|
766
|
+
const total = products.reduce((sum, p) => sum + p.price * p.quantity, 0);
|
|
767
|
+
const taxTotal = products.reduce((sum, p) => sum + p.price * p.quantity * p.tax, 0);
|
|
768
|
+
const completeDateTimestamp = row[keyIndices["Basket.completeDate"]];
|
|
769
|
+
const completeDate = completeDateTimestamp ? new Date(completeDateTimestamp).toISOString() : "";
|
|
770
|
+
return {
|
|
771
|
+
id: row[keyIndices["Basket.id"]],
|
|
772
|
+
name: row[keyIndices["Basket.name"]],
|
|
773
|
+
consumerId: row[keyIndices["Basket.consumerId"]],
|
|
774
|
+
products,
|
|
775
|
+
total,
|
|
776
|
+
taxTotal,
|
|
777
|
+
status: "completed",
|
|
778
|
+
completeDate
|
|
779
|
+
};
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
/** Unique non-null consumer ids referenced by a set of baskets. */
|
|
783
|
+
function collectConsumerIds(baskets) {
|
|
784
|
+
if (!baskets.length) return null;
|
|
785
|
+
const ids = baskets.map((b) => b.consumerId).filter((id) => id != null);
|
|
786
|
+
return ids.length > 0 ? [...new Set(ids)] : null;
|
|
787
|
+
}
|
|
788
|
+
/** Unique product ids referenced by a set of baskets. */
|
|
789
|
+
function collectProductIds(baskets) {
|
|
790
|
+
if (!baskets.length) return null;
|
|
791
|
+
const ids = /* @__PURE__ */ new Set();
|
|
792
|
+
baskets.forEach((basket) => {
|
|
793
|
+
basket.products.forEach((product) => {
|
|
794
|
+
if (product.productId) ids.add(product.productId);
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
return ids.size > 0 ? [...ids] : null;
|
|
798
|
+
}
|
|
799
|
+
/** Joins baskets with consumer + product lookups, applies search, sorts. */
|
|
800
|
+
function combineBasketRows({ baskets, consumerMap, productMap, search }) {
|
|
801
|
+
if (!baskets.length) return [];
|
|
802
|
+
let combinedRows = baskets.map((basket) => {
|
|
803
|
+
const consumer = basket.consumerId ? consumerMap[basket.consumerId] : null;
|
|
804
|
+
const productsWithNames = basket.products.map((p) => {
|
|
805
|
+
const product = productMap[p.productId];
|
|
806
|
+
return {
|
|
807
|
+
...p,
|
|
808
|
+
productName: product?.name || `Product #${p.productId}`
|
|
809
|
+
};
|
|
810
|
+
});
|
|
811
|
+
return {
|
|
812
|
+
id: basket.id,
|
|
813
|
+
basketId: basket.id,
|
|
814
|
+
basketName: basket.name || `Basket #${basket.id}`,
|
|
815
|
+
consumerId: basket.consumerId,
|
|
816
|
+
consumerName: consumer ? `${consumer.firstName} ${consumer.lastName}` : "Unknown",
|
|
817
|
+
consumerEmail: consumer?.email || "-",
|
|
818
|
+
products: productsWithNames,
|
|
819
|
+
basketTotal: (basket.total || 0) / 100,
|
|
820
|
+
tax: basket.taxTotal || 0,
|
|
821
|
+
dateTime: basket.completeDate,
|
|
822
|
+
status: basket.status || "pending"
|
|
823
|
+
};
|
|
824
|
+
});
|
|
825
|
+
if (search) {
|
|
826
|
+
const searchLower = search.toLowerCase();
|
|
827
|
+
combinedRows = combinedRows.filter((row) => row.basketId.toString().includes(searchLower) || row.basketName.toLowerCase().includes(searchLower) || row.consumerName.toLowerCase().includes(searchLower) || row.consumerEmail.toLowerCase().includes(searchLower));
|
|
828
|
+
}
|
|
829
|
+
combinedRows.sort((a, b) => {
|
|
830
|
+
const dateA = new Date(a.dateTime).getTime();
|
|
831
|
+
return new Date(b.dateTime).getTime() - dateA;
|
|
832
|
+
});
|
|
833
|
+
return combinedRows;
|
|
834
|
+
}
|
|
835
|
+
/** Aggregate totals for a set of report rows. */
|
|
836
|
+
function computeBasketSummary(rows) {
|
|
837
|
+
const uniqueConsumerIds = new Set(rows.map((row) => row.consumerId));
|
|
838
|
+
return {
|
|
839
|
+
totalBaskets: rows.length,
|
|
840
|
+
totalRevenue: rows.reduce((sum, row) => sum + row.basketTotal, 0),
|
|
841
|
+
totalTax: rows.reduce((sum, row) => sum + row.tax, 0),
|
|
842
|
+
uniqueConsumers: uniqueConsumerIds.size
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/** Builds a consumer id → consumer lookup. */
|
|
846
|
+
function buildConsumerMap(consumers) {
|
|
847
|
+
const map = {};
|
|
848
|
+
if (consumers) consumers.forEach((consumer) => {
|
|
849
|
+
map[consumer.id] = consumer;
|
|
850
|
+
});
|
|
851
|
+
return map;
|
|
852
|
+
}
|
|
853
|
+
|
|
840
854
|
//#endregion
|
|
841
855
|
//#region src/core/merchant.ts
|
|
842
856
|
const MERCHANT_PATH = `/api/${SHOPWAVE_RESOURCES.merchant.route}`;
|