hookwright 1.1.1 → 1.2.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.
- package/README.md +12 -1
- package/dist/cli.js +168 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,17 @@ Each provider owns its gate, signature scheme, field map, payload shape and summ
|
|
|
124
124
|
- **Razorpay Magic** is root-level, uses capitalised shipping keys (`Shipping_address.Address1`) and sends `line_items_total` as a **string in paise** — matching [Razorpay's abandoned cart webhook docs](https://razorpay.com/docs/payments/magic-checkout/abandoned-cart/). Its gate is "non-empty `abandoned_checkout_url`" rather than an exact value.
|
|
125
125
|
- **Nitro** keys off `eventName` and requires `eventVal.customer.phone` on *every* event, parsed against root `country`. Choose the event with `--event`, or from the menu in the UI.
|
|
126
126
|
|
|
127
|
+
Nitro's eight events are about different things, so each asks for a different subject:
|
|
128
|
+
|
|
129
|
+
| event | you pick | cart |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `view` | nothing | — |
|
|
132
|
+
| `category_view` | one collection (fetched from Shopify, custom **and** smart) | — |
|
|
133
|
+
| `product_view` | one product | — |
|
|
134
|
+
| `addtocart` · `removefromcart` · `checkout` · `orders/create` · `orders/updated` | products and quantities | ✅ |
|
|
135
|
+
|
|
136
|
+
A page view has no cart, so it is never asked for a discount; a category view emits the collection's real id, title and URL and carries no line items.
|
|
137
|
+
|
|
127
138
|
Adding one is a single file in `src/providers/` exporting `buildPayload`, `sign`, `webhookUrl`, `summarize`, a `gate` and a `fieldMap` — see [docs/cashfree-occ.md](docs/cashfree-occ.md).
|
|
128
139
|
|
|
129
140
|
## Development
|
|
@@ -131,7 +142,7 @@ Adding one is a single file in `src/providers/` exporting `buildPayload`, `sign`
|
|
|
131
142
|
```bash
|
|
132
143
|
npm install
|
|
133
144
|
npm run dev # esbuild watch
|
|
134
|
-
npm test #
|
|
145
|
+
npm test # 185 tests, no network required
|
|
135
146
|
npm start # build + run
|
|
136
147
|
```
|
|
137
148
|
|
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ var init_package = __esm({
|
|
|
24
24
|
"package.json"() {
|
|
25
25
|
package_default = {
|
|
26
26
|
name: "hookwright",
|
|
27
|
-
version: "1.
|
|
27
|
+
version: "1.2.0",
|
|
28
28
|
description: "Build real, signed e-commerce webhooks from a live Shopify catalogue \u2014 interactive terminal UI, no backend",
|
|
29
29
|
keywords: [
|
|
30
30
|
"webhook",
|
|
@@ -407,6 +407,16 @@ function cartPermalink(domain, items) {
|
|
|
407
407
|
function money2(value) {
|
|
408
408
|
return Math.round(Number(value) * 100) / 100;
|
|
409
409
|
}
|
|
410
|
+
function normalizeCollection(collection) {
|
|
411
|
+
return {
|
|
412
|
+
id: String(collection.id),
|
|
413
|
+
title: collection.title,
|
|
414
|
+
handle: collection.handle,
|
|
415
|
+
kind: collection.kind,
|
|
416
|
+
image: collection.image?.src ?? "",
|
|
417
|
+
productsCount: collection.products_count ?? null
|
|
418
|
+
};
|
|
419
|
+
}
|
|
410
420
|
|
|
411
421
|
// src/providers/cashfree-occ.mjs
|
|
412
422
|
var GATE_PATH = "type";
|
|
@@ -757,15 +767,18 @@ var razorpay_magic_default = {
|
|
|
757
767
|
// src/providers/nitro.mjs
|
|
758
768
|
import crypto3 from "node:crypto";
|
|
759
769
|
var EVENTS = [
|
|
760
|
-
{ eventName: "view", type: "nitro_view", label: "Page View" },
|
|
761
|
-
{ eventName: "category_view", type: "nitro_category_view", label: "Category View" },
|
|
762
|
-
{ eventName: "product_view", type: "nitro_product_view", label: "Product View" },
|
|
763
|
-
{ eventName: "addtocart", type: "nitro_addtocart", label: "Add To Cart" },
|
|
764
|
-
{ eventName: "removefromcart", type: "nitro_removefromcart", label: "Remove From Cart" },
|
|
765
|
-
{ eventName: "checkout", type: "nitro_checkout", label: "Checkout" },
|
|
766
|
-
{ eventName: "orders/create", type: "nitro_orders_create", label: "Order Created" },
|
|
767
|
-
{ eventName: "orders/updated", type: "nitro_orders_updated", label: "Order Updated" }
|
|
770
|
+
{ eventName: "view", type: "nitro_view", label: "Page View", selection: "none", subject: "a page, no product" },
|
|
771
|
+
{ eventName: "category_view", type: "nitro_category_view", label: "Category View", selection: "collection", subject: "one collection" },
|
|
772
|
+
{ eventName: "product_view", type: "nitro_product_view", label: "Product View", selection: "product", subject: "one product" },
|
|
773
|
+
{ eventName: "addtocart", type: "nitro_addtocart", label: "Add To Cart", selection: "cart", subject: "cart contents" },
|
|
774
|
+
{ eventName: "removefromcart", type: "nitro_removefromcart", label: "Remove From Cart", selection: "cart", subject: "cart contents" },
|
|
775
|
+
{ eventName: "checkout", type: "nitro_checkout", label: "Checkout", selection: "cart", subject: "cart contents" },
|
|
776
|
+
{ eventName: "orders/create", type: "nitro_orders_create", label: "Order Created", selection: "cart", subject: "ordered items" },
|
|
777
|
+
{ eventName: "orders/updated", type: "nitro_orders_updated", label: "Order Updated", selection: "cart", subject: "ordered items" }
|
|
768
778
|
];
|
|
779
|
+
function selectionFor(eventName) {
|
|
780
|
+
return EVENTS.find((e) => e.eventName === eventName)?.selection ?? "cart";
|
|
781
|
+
}
|
|
769
782
|
var DEFAULT_EVENT = "addtocart";
|
|
770
783
|
var COMMON_FIELDS = [
|
|
771
784
|
{ source: "eventName", target: "\xABgate\xBB", required: true, note: "must match the schema exactly" },
|
|
@@ -783,8 +796,9 @@ var FIELD_MAPS = {
|
|
|
783
796
|
category_view: [
|
|
784
797
|
...COMMON_FIELDS,
|
|
785
798
|
...PAGE_FIELDS,
|
|
786
|
-
{ source: "eventVal.resource_id", target: "eventVal.resource_id", required: true },
|
|
799
|
+
{ source: "eventVal.resource_id", target: "eventVal.resource_id", required: true, note: "the collection id" },
|
|
787
800
|
{ source: "eventVal.resource", target: "eventVal.resource" },
|
|
801
|
+
{ source: "eventVal.title", target: "collection name", required: true },
|
|
788
802
|
{ source: "eventVal.domain", target: "eventVal.domain" }
|
|
789
803
|
],
|
|
790
804
|
product_view: [
|
|
@@ -848,6 +862,17 @@ var EDITABLE = {
|
|
|
848
862
|
{ group: "Page", path: "eventVal.l", label: "Location URL" },
|
|
849
863
|
{ group: "Page", path: "eventVal._ss", label: "Session id" }
|
|
850
864
|
],
|
|
865
|
+
collection: [
|
|
866
|
+
{ group: "Collection", path: "eventVal.resource_id", label: "Collection id" },
|
|
867
|
+
{ group: "Collection", path: "eventVal.title", label: "Collection name" },
|
|
868
|
+
{ group: "Collection", path: "eventVal.l", label: "Collection URL" }
|
|
869
|
+
],
|
|
870
|
+
product: [
|
|
871
|
+
{ group: "Product", path: "eventVal.resource_id", label: "Product id" },
|
|
872
|
+
{ group: "Product", path: "eventVal.title", label: "Product title" },
|
|
873
|
+
{ group: "Product", path: "eventVal.price", label: "Price", type: "number" },
|
|
874
|
+
{ group: "Product", path: "eventVal.image", label: "Image URL" }
|
|
875
|
+
],
|
|
851
876
|
cart: [
|
|
852
877
|
{ group: "Cart", path: "eventVal.cart_value", label: "Cart value", type: "number" },
|
|
853
878
|
{ group: "Cart", path: "eventVal.init_cart", label: "Init cart" },
|
|
@@ -866,7 +891,8 @@ var EDITABLE = {
|
|
|
866
891
|
function editableFieldsFor(eventName) {
|
|
867
892
|
const e = EDITABLE;
|
|
868
893
|
if (eventName === "view") return [...e.common, ...e.page];
|
|
869
|
-
if (eventName === "category_view"
|
|
894
|
+
if (eventName === "category_view") return [...e.common, ...e.page, ...e.collection];
|
|
895
|
+
if (eventName === "product_view") return [...e.common, ...e.page, ...e.product];
|
|
870
896
|
if (eventName === "addtocart" || eventName === "removefromcart") return [...e.common, ...e.cart];
|
|
871
897
|
if (eventName === "checkout") return [...e.common, ...e.cart];
|
|
872
898
|
if (eventName.startsWith("orders/")) return [...e.common, ...e.order];
|
|
@@ -919,17 +945,22 @@ function buildPayload3(ctx) {
|
|
|
919
945
|
let eventVal;
|
|
920
946
|
switch (eventName) {
|
|
921
947
|
case "view":
|
|
922
|
-
eventVal = {
|
|
948
|
+
eventVal = { page: "home", h: domain, l: `https://${domain}`, _ss: session, domain, customer };
|
|
923
949
|
break;
|
|
924
|
-
case "category_view":
|
|
950
|
+
case "category_view": {
|
|
951
|
+
const collection = ctx.collection;
|
|
925
952
|
eventVal = {
|
|
926
953
|
...page,
|
|
954
|
+
page: "collection",
|
|
927
955
|
resource: "collection",
|
|
928
|
-
resource_id: String(
|
|
929
|
-
|
|
956
|
+
resource_id: String(collection?.id ?? ""),
|
|
957
|
+
title: collection?.title,
|
|
958
|
+
image: collection?.image || void 0,
|
|
959
|
+
l: collection?.handle ? `https://${domain}/collections/${collection.handle}` : `https://${domain}/collections/all`,
|
|
930
960
|
customer
|
|
931
961
|
};
|
|
932
962
|
break;
|
|
963
|
+
}
|
|
933
964
|
case "product_view":
|
|
934
965
|
eventVal = {
|
|
935
966
|
...page,
|
|
@@ -1029,6 +1060,7 @@ var nitro_default = {
|
|
|
1029
1060
|
fieldMapFor: (eventName) => FIELD_MAPS[eventName] ?? FIELD_MAPS[DEFAULT_EVENT],
|
|
1030
1061
|
editableFields: editableFields3,
|
|
1031
1062
|
editableFieldsFor,
|
|
1063
|
+
selectionFor,
|
|
1032
1064
|
buildPayload: buildPayload3,
|
|
1033
1065
|
sign: sign3,
|
|
1034
1066
|
webhookUrl: webhookUrl3,
|
|
@@ -1378,6 +1410,20 @@ var ShopifyClient = class {
|
|
|
1378
1410
|
const { json } = await this.request("/products/count.json");
|
|
1379
1411
|
return json?.count ?? 0;
|
|
1380
1412
|
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Collections, from both endpoints Shopify splits them across: custom
|
|
1415
|
+
* (manually curated) and smart (rule based). Either can back a category page.
|
|
1416
|
+
*/
|
|
1417
|
+
async collections({ limit = 50, title } = {}) {
|
|
1418
|
+
const [custom, smart] = await Promise.all([
|
|
1419
|
+
this.request("/custom_collections.json", { query: { limit, title } }).catch(() => ({ json: null })),
|
|
1420
|
+
this.request("/smart_collections.json", { query: { limit, title } }).catch(() => ({ json: null }))
|
|
1421
|
+
]);
|
|
1422
|
+
return [
|
|
1423
|
+
...(custom.json?.custom_collections ?? []).map((c2) => ({ ...c2, kind: "custom" })),
|
|
1424
|
+
...(smart.json?.smart_collections ?? []).map((c2) => ({ ...c2, kind: "smart" }))
|
|
1425
|
+
];
|
|
1426
|
+
}
|
|
1381
1427
|
};
|
|
1382
1428
|
function normalizeDomain(domain) {
|
|
1383
1429
|
return String(domain).trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "").toLowerCase();
|
|
@@ -1403,6 +1449,11 @@ async function fetchCatalogue(cfg, { search, pages = 1, limit = 50 } = {}) {
|
|
|
1403
1449
|
const raw = await client.products({ title: search, pages, limit });
|
|
1404
1450
|
return raw.map(normalizeProduct).filter((p) => p.variants.length);
|
|
1405
1451
|
}
|
|
1452
|
+
async function fetchCollections(cfg, { search, limit = 50 } = {}) {
|
|
1453
|
+
const client = new ShopifyClient(cfg.shopify);
|
|
1454
|
+
const raw = await client.collections({ title: search, limit });
|
|
1455
|
+
return raw.map(normalizeCollection);
|
|
1456
|
+
}
|
|
1406
1457
|
function autoSelect(products, count = 1) {
|
|
1407
1458
|
return products.slice(0, count).map((product) => ({
|
|
1408
1459
|
product,
|
|
@@ -1808,7 +1859,7 @@ function loadPayload(file) {
|
|
|
1808
1859
|
}
|
|
1809
1860
|
|
|
1810
1861
|
// src/core/build.mjs
|
|
1811
|
-
function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName }) {
|
|
1862
|
+
function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, eventName, collection }) {
|
|
1812
1863
|
const provider = getProvider(providerId);
|
|
1813
1864
|
const event = eventName ?? provider.defaultEvent;
|
|
1814
1865
|
const resolvedPhone = phone ?? cfg.customer.phone;
|
|
@@ -1818,6 +1869,7 @@ function draftPayload({ cfg, providerId = "cashfree-occ", items, discount = 0, p
|
|
|
1818
1869
|
items,
|
|
1819
1870
|
discount,
|
|
1820
1871
|
eventName: event,
|
|
1872
|
+
collection,
|
|
1821
1873
|
phone: parsed.ok ? parsed.value : String(resolvedPhone ?? "")
|
|
1822
1874
|
});
|
|
1823
1875
|
const editable = provider.editableFieldsFor ? provider.editableFieldsFor(event) : provider.editableFields;
|
|
@@ -1833,14 +1885,16 @@ function applyEdits(payload, fields, edits) {
|
|
|
1833
1885
|
}
|
|
1834
1886
|
return payload;
|
|
1835
1887
|
}
|
|
1836
|
-
async function buildPayload4({ cfg, providerId = "cashfree-occ", items, discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName }) {
|
|
1837
|
-
|
|
1888
|
+
async function buildPayload4({ cfg, providerId = "cashfree-occ", items = [], discount = 0, phone, checkImageUrls = true, payload: prebuilt, eventName, collection }) {
|
|
1889
|
+
const provider0 = getProvider(providerId);
|
|
1890
|
+
const needsItems = !provider0.selectionFor || ["cart", "product"].includes(provider0.selectionFor(eventName ?? provider0.defaultEvent));
|
|
1891
|
+
if (needsItems && !items.length) throw new ConfigError("no products selected");
|
|
1838
1892
|
const provider = getProvider(providerId);
|
|
1839
1893
|
const resolvedPhone = phone ?? cfg.customer.phone;
|
|
1840
1894
|
const parsed = toE164(resolvedPhone, cfg.customer.countryCode);
|
|
1841
1895
|
if (!parsed.ok) throw new ConfigError(`phone is unusable: ${parsed.reason}`);
|
|
1842
1896
|
const event = eventName ?? provider.defaultEvent;
|
|
1843
|
-
const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event });
|
|
1897
|
+
const payload = prebuilt ?? provider.buildPayload({ config: cfg, items, discount, phone: parsed.value, eventName: event, collection });
|
|
1844
1898
|
const body = JSON.stringify(payload);
|
|
1845
1899
|
const report = await runAll({ payload, body, provider, config: cfg, checkImageUrls, eventName: event });
|
|
1846
1900
|
const summary = provider.summarize ? provider.summarize(payload) : {};
|
|
@@ -2040,6 +2094,7 @@ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
2040
2094
|
var STEP = {
|
|
2041
2095
|
EVENT: "event",
|
|
2042
2096
|
LOADING: "loading",
|
|
2097
|
+
COLLECTION: "collection",
|
|
2043
2098
|
PICK: "pick",
|
|
2044
2099
|
VARIANT: "variant",
|
|
2045
2100
|
QUANTITY: "quantity",
|
|
@@ -2060,6 +2115,8 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2060
2115
|
const [eventName, setEventName] = useState(provider.defaultEvent);
|
|
2061
2116
|
const [step, setStep] = useState(multiEvent ? STEP.EVENT : STEP.LOADING);
|
|
2062
2117
|
const [products, setProducts] = useState([]);
|
|
2118
|
+
const [collections, setCollections] = useState([]);
|
|
2119
|
+
const [collection, setCollection] = useState(null);
|
|
2063
2120
|
const [chosen, setChosen] = useState([]);
|
|
2064
2121
|
const [cursor, setCursor] = useState(0);
|
|
2065
2122
|
const [items, setItems] = useState([]);
|
|
@@ -2079,6 +2136,7 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2079
2136
|
case STEP.EVENT:
|
|
2080
2137
|
return onDone();
|
|
2081
2138
|
case STEP.PICK:
|
|
2139
|
+
case STEP.COLLECTION:
|
|
2082
2140
|
return multiEvent ? setStep(STEP.EVENT) : onDone();
|
|
2083
2141
|
case STEP.VARIANT:
|
|
2084
2142
|
if (cursor === 0) return setStep(STEP.PICK);
|
|
@@ -2090,6 +2148,8 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2090
2148
|
setItems(items.slice(0, -1));
|
|
2091
2149
|
return stepInto(cursor - 1);
|
|
2092
2150
|
case STEP.DISCOUNT: {
|
|
2151
|
+
if (selection === "none") return multiEvent ? setStep(STEP.EVENT) : onDone();
|
|
2152
|
+
if (selection === "collection") return setStep(STEP.COLLECTION);
|
|
2093
2153
|
const last = chosen.length - 1;
|
|
2094
2154
|
setItems(items.slice(0, -1));
|
|
2095
2155
|
return stepInto(last);
|
|
@@ -2130,9 +2190,36 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2130
2190
|
if (input === "j" && built) copy(built.body).then((r) => setCopied({ ...r, what: "payload JSON" }));
|
|
2131
2191
|
if ((input === "s" || key.return) && built?.report.ok) doSend();
|
|
2132
2192
|
});
|
|
2193
|
+
const selection = provider.selectionFor ? provider.selectionFor(eventName) : "cart";
|
|
2133
2194
|
useEffect(() => {
|
|
2134
2195
|
if (step === STEP.EVENT) return;
|
|
2135
2196
|
let alive = true;
|
|
2197
|
+
if (selection === "none") {
|
|
2198
|
+
startReview(0);
|
|
2199
|
+
return () => {
|
|
2200
|
+
alive = false;
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
if (selection === "collection") {
|
|
2204
|
+
fetchCollections(config).then((list) => {
|
|
2205
|
+
if (!alive) return;
|
|
2206
|
+
if (!list.length) {
|
|
2207
|
+
setError({ message: "this store has no collections", hint: "create one in Shopify, or pick a different event" });
|
|
2208
|
+
setStep(STEP.ERROR);
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
setCollections(list);
|
|
2212
|
+
setStep(STEP.COLLECTION);
|
|
2213
|
+
}).catch((err) => {
|
|
2214
|
+
if (alive) {
|
|
2215
|
+
setError({ message: err.message, hint: err.hint });
|
|
2216
|
+
setStep(STEP.ERROR);
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
return () => {
|
|
2220
|
+
alive = false;
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2136
2223
|
fetchCatalogue(config).then((list) => {
|
|
2137
2224
|
if (!alive) return;
|
|
2138
2225
|
if (!list.length) {
|
|
@@ -2150,11 +2237,12 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2150
2237
|
return () => {
|
|
2151
2238
|
alive = false;
|
|
2152
2239
|
};
|
|
2153
|
-
}, [step === STEP.EVENT]);
|
|
2240
|
+
}, [step === STEP.EVENT, eventName]);
|
|
2154
2241
|
const advance = (nextItems, nextCursor) => {
|
|
2155
2242
|
if (nextCursor >= chosen.length) {
|
|
2156
2243
|
setItems(nextItems);
|
|
2157
|
-
setStep(STEP.DISCOUNT);
|
|
2244
|
+
if (selection === "cart") setStep(STEP.DISCOUNT);
|
|
2245
|
+
else startReview(0, collection, nextItems);
|
|
2158
2246
|
return;
|
|
2159
2247
|
}
|
|
2160
2248
|
setItems(nextItems);
|
|
@@ -2167,6 +2255,24 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2167
2255
|
setStep(STEP.QUANTITY);
|
|
2168
2256
|
}
|
|
2169
2257
|
};
|
|
2258
|
+
const onSingleProductPicked = (id) => {
|
|
2259
|
+
const product = products.find((p) => p.id === id);
|
|
2260
|
+
if (!product) return;
|
|
2261
|
+
setChosen([product]);
|
|
2262
|
+
setCursor(0);
|
|
2263
|
+
setItems([]);
|
|
2264
|
+
if (product.variants.length > 1) setStep(STEP.VARIANT);
|
|
2265
|
+
else {
|
|
2266
|
+
setPendingVariant(product.variants[0]);
|
|
2267
|
+
setStep(STEP.QUANTITY);
|
|
2268
|
+
}
|
|
2269
|
+
};
|
|
2270
|
+
const onCollectionPicked = (id) => {
|
|
2271
|
+
const picked = collections.find((c2) => c2.id === id);
|
|
2272
|
+
if (!picked) return;
|
|
2273
|
+
setCollection(picked);
|
|
2274
|
+
startReview(0, picked);
|
|
2275
|
+
};
|
|
2170
2276
|
const onProductsPicked = (ids) => {
|
|
2171
2277
|
const picked = ids.map((id) => products.find((p) => p.id === id)).filter(Boolean);
|
|
2172
2278
|
if (!picked.length) return;
|
|
@@ -2191,10 +2297,16 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2191
2297
|
const nextItems = [...items, { product, variant: pendingVariant, quantity: qty }];
|
|
2192
2298
|
advance(nextItems, cursor + 1);
|
|
2193
2299
|
};
|
|
2194
|
-
const
|
|
2195
|
-
const discount = Math.max(0, Number.parseFloat(raw) || 0);
|
|
2300
|
+
const startReview = (discount, chosenCollection = collection, chosenItems = items) => {
|
|
2196
2301
|
try {
|
|
2197
|
-
const next = draftPayload({
|
|
2302
|
+
const next = draftPayload({
|
|
2303
|
+
cfg: config,
|
|
2304
|
+
providerId,
|
|
2305
|
+
items: chosenItems,
|
|
2306
|
+
discount,
|
|
2307
|
+
eventName,
|
|
2308
|
+
collection: chosenCollection
|
|
2309
|
+
});
|
|
2198
2310
|
setDraft({ ...next, discount });
|
|
2199
2311
|
setStep(STEP.MODE);
|
|
2200
2312
|
} catch (err) {
|
|
@@ -2202,9 +2314,10 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2202
2314
|
setStep(STEP.ERROR);
|
|
2203
2315
|
}
|
|
2204
2316
|
};
|
|
2317
|
+
const onDiscount = (raw) => startReview(Math.max(0, Number.parseFloat(raw) || 0));
|
|
2205
2318
|
const finalise = (payload) => {
|
|
2206
2319
|
setStep(STEP.BUILDING);
|
|
2207
|
-
buildPayload4({ cfg: config, providerId, items, discount: draft.discount, payload, eventName }).then(async (result) => {
|
|
2320
|
+
buildPayload4({ cfg: config, providerId, items, discount: draft.discount, payload, eventName, collection }).then(async (result) => {
|
|
2208
2321
|
setBuilt(result);
|
|
2209
2322
|
let req = null;
|
|
2210
2323
|
let command = "";
|
|
@@ -2278,9 +2391,37 @@ function Build({ config, providerId = "cashfree-occ", onDone }) {
|
|
|
2278
2391
|
) })
|
|
2279
2392
|
] }),
|
|
2280
2393
|
step === STEP.LOADING && /* @__PURE__ */ jsx8(Spinner, { label: `fetching live products from ${store}\u2026` }),
|
|
2281
|
-
step === STEP.
|
|
2394
|
+
step === STEP.COLLECTION && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2395
|
+
/* @__PURE__ */ jsx8(Text8, { children: "Which collection was viewed?" }),
|
|
2396
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|
|
2397
|
+
Select4,
|
|
2398
|
+
{
|
|
2399
|
+
visibleOptionCount: 10,
|
|
2400
|
+
options: collections.map((c2) => ({
|
|
2401
|
+
label: `${truncate(c2.title, 40).padEnd(41)} ${c2.productsCount != null ? `${c2.productsCount} products` : c2.kind}`,
|
|
2402
|
+
value: c2.id
|
|
2403
|
+
})),
|
|
2404
|
+
onChange: onCollectionPicked
|
|
2405
|
+
}
|
|
2406
|
+
) })
|
|
2407
|
+
] }),
|
|
2408
|
+
step === STEP.PICK && selection === "product" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2409
|
+
/* @__PURE__ */ jsx8(Text8, { children: "Which product was viewed?" }),
|
|
2410
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|
|
2411
|
+
Select4,
|
|
2412
|
+
{
|
|
2413
|
+
visibleOptionCount: 10,
|
|
2414
|
+
options: products.map((p) => ({
|
|
2415
|
+
label: `${truncate(p.title, 44).padEnd(45)} ${money(p.variants[0]?.price, currency)}`,
|
|
2416
|
+
value: p.id
|
|
2417
|
+
})),
|
|
2418
|
+
onChange: onSingleProductPicked
|
|
2419
|
+
}
|
|
2420
|
+
) })
|
|
2421
|
+
] }),
|
|
2422
|
+
step === STEP.PICK && selection !== "product" && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
2282
2423
|
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
2283
|
-
`Select the products in the
|
|
2424
|
+
`Select the products in the cart `,
|
|
2284
2425
|
/* @__PURE__ */ jsx8(Text8, { color: palette.dim, children: "(space toggles \xB7 enter confirms)" })
|
|
2285
2426
|
] }),
|
|
2286
2427
|
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(
|