@burdenoff/microfe-coolandlovely 2026.1026.1 → 2026.1027.2
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/dist/coolandlovely/components/AddToCartPanel.d.ts.map +1 -1
- package/dist/coolandlovely/components/AddToCartPanel.js +63 -47
- package/dist/coolandlovely/components/AddToCartPanel.js.map +1 -1
- package/dist/coolandlovely/components/CollaborationFeeCard.d.ts +9 -0
- package/dist/coolandlovely/components/CollaborationFeeCard.d.ts.map +1 -0
- package/dist/coolandlovely/components/CollaborationFeeCard.js +98 -0
- package/dist/coolandlovely/components/CollaborationFeeCard.js.map +1 -0
- package/dist/coolandlovely/components/PayForOrder.d.ts +11 -3
- package/dist/coolandlovely/components/PayForOrder.d.ts.map +1 -1
- package/dist/coolandlovely/components/PayForOrder.js +13 -13
- package/dist/coolandlovely/components/PayForOrder.js.map +1 -1
- package/dist/coolandlovely/components/RefundPanel.d.ts +8 -0
- package/dist/coolandlovely/components/RefundPanel.d.ts.map +1 -0
- package/dist/coolandlovely/components/RefundPanel.js +113 -0
- package/dist/coolandlovely/components/RefundPanel.js.map +1 -0
- package/dist/coolandlovely/components/StatusBadge.d.ts.map +1 -1
- package/dist/coolandlovely/components/StatusBadge.js +11 -1
- package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
- package/dist/coolandlovely/components/StockEditor.d.ts +8 -0
- package/dist/coolandlovely/components/StockEditor.d.ts.map +1 -0
- package/dist/coolandlovely/components/StockEditor.js +147 -0
- package/dist/coolandlovely/components/StockEditor.js.map +1 -0
- package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts +6 -1
- package/dist/coolandlovely/hooks/useBrandOrdersApi.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useBrandOrdersApi.js +62 -30
- package/dist/coolandlovely/hooks/useBrandOrdersApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useCommerceApi.d.ts +10 -2
- package/dist/coolandlovely/hooks/useCommerceApi.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useCommerceApi.js +118 -75
- package/dist/coolandlovely/hooks/useCommerceApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts +2 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +71 -66
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useInventoryApi.d.ts +14 -0
- package/dist/coolandlovely/hooks/useInventoryApi.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useInventoryApi.js +46 -0
- package/dist/coolandlovely/hooks/useInventoryApi.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +111 -109
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/commerce/BrandOrdersPage.d.ts +2 -1
- package/dist/coolandlovely/pages/commerce/BrandOrdersPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/commerce/BrandOrdersPage.js +94 -92
- package/dist/coolandlovely/pages/commerce/BrandOrdersPage.js.map +1 -1
- package/dist/coolandlovely/pages/commerce/CartPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/commerce/CartPage.js +6 -3
- package/dist/coolandlovely/pages/commerce/CartPage.js.map +1 -1
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.d.ts +3 -0
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js +183 -104
- package/dist/coolandlovely/pages/commerce/CreatorEarningsPage.js.map +1 -1
- package/dist/coolandlovely/pages/commerce/OrdersPage.d.ts +2 -1
- package/dist/coolandlovely/pages/commerce/OrdersPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/commerce/OrdersPage.js +121 -59
- package/dist/coolandlovely/pages/commerce/OrdersPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +140 -135
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
- package/dist/coolandlovely/types/commerce.d.ts +94 -3
- package/dist/coolandlovely/types/commerce.d.ts.map +1 -1
- package/dist/coolandlovely/types/index.d.ts +3 -0
- package/dist/coolandlovely/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddToCartPanel.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"AddToCartPanel.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAE,EAAY,MAAM,OAAO,CAAC;AAO1C,OAAO,KAAK,EAAE,YAAY,EAAyB,MAAM,UAAU,CAAC;AAGpE,UAAU,mBAAmB;IAC3B,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC1E,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAkCD,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CA6JlD,CAAC"}
|
|
@@ -3,95 +3,111 @@ import { cn as t } from "../utils/cn.js";
|
|
|
3
3
|
import { useTr as n } from "../hooks/useTr.js";
|
|
4
4
|
import "../hooks/index.js";
|
|
5
5
|
import { useAddToCart as r } from "../hooks/useCommerceApi.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { useStyleItemInventory as i } from "../hooks/useInventoryApi.js";
|
|
7
|
+
import { useState as a } from "react";
|
|
8
|
+
import { Check as o, Loader2 as s, ShoppingBag as c, Sparkles as l } from "lucide-react";
|
|
9
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
10
|
//#region src/coolandlovely/components/AddToCartPanel.tsx
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
return
|
|
11
|
+
var f = 10, p = 5;
|
|
12
|
+
function m(e, t, n) {
|
|
13
|
+
return e?.trackInventory ? e.variants.filter((e) => (t === null || (e.size ?? "") === t) && (n === null || (e.colour ?? "") === n)).reduce((e, t) => e + t.stockQuantity, 0) : null;
|
|
14
|
+
}
|
|
15
|
+
var h = (e) => t("inline-flex min-w-9 items-center justify-center rounded-full border px-3 py-1 text-sm font-medium transition-colors", e ? "border-action-primary-bg bg-action-primary-bg text-action-primary-text" : "border-border-seam text-text-primary hover:bg-bg-sunken"), g = ({ item: g, lookId: _ }) => {
|
|
16
|
+
let v = n(), { navigateTo: y } = e(), b = r(), x = i(g.id), [S, C] = a(g.sizes.length === 1 ? g.sizes[0] ?? null : null), [w, T] = a(g.colours.length === 1 ? g.colours[0] ?? null : null), [E, D] = a(1), O = g.status === "ACTIVE" && g.price > 0, k = g.sizes.length > 0 && !S, A = g.colours.length > 0 && !w, j = x.data, M = k || A ? null : m(j, S ?? "", w ?? ""), N = M !== null && M <= 0, P = M === null ? f : Math.max(1, Math.min(f, M));
|
|
17
|
+
return O ? /* @__PURE__ */ d("div", {
|
|
13
18
|
className: "space-y-4",
|
|
14
19
|
"data-testid": "add-to-cart-panel",
|
|
15
20
|
children: [
|
|
16
|
-
|
|
21
|
+
_ && /* @__PURE__ */ d("p", {
|
|
17
22
|
className: "inline-flex items-center gap-1.5 text-xs font-medium text-accent-primary",
|
|
18
|
-
children: [/* @__PURE__ */ l
|
|
23
|
+
children: [/* @__PURE__ */ u(l, { className: "h-3.5 w-3.5" }), v("coolandlovely.cart.shoppingLook", "Shopping from a look — your purchase supports its creator")]
|
|
19
24
|
}),
|
|
20
|
-
|
|
25
|
+
g.sizes.length > 0 && /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
21
26
|
className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
22
|
-
children:
|
|
23
|
-
}), /* @__PURE__ */
|
|
27
|
+
children: v("coolandlovely.cart.chooseSize", "Choose a size")
|
|
28
|
+
}), /* @__PURE__ */ u("div", {
|
|
24
29
|
className: "flex flex-wrap gap-2",
|
|
25
|
-
children:
|
|
30
|
+
children: g.sizes.map((e) => /* @__PURE__ */ u("button", {
|
|
26
31
|
type: "button",
|
|
27
|
-
"aria-pressed":
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
"aria-pressed": S === e,
|
|
33
|
+
disabled: m(j, e, w) === 0,
|
|
34
|
+
onClick: () => {
|
|
35
|
+
C(e), D(1);
|
|
36
|
+
},
|
|
37
|
+
className: t(h(S === e), "disabled:opacity-40 disabled:line-through"),
|
|
30
38
|
children: e
|
|
31
39
|
}, e))
|
|
32
40
|
})] }),
|
|
33
|
-
|
|
41
|
+
g.colours.length > 0 && /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
34
42
|
className: "mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
35
|
-
children:
|
|
36
|
-
}), /* @__PURE__ */
|
|
43
|
+
children: v("coolandlovely.cart.chooseColour", "Choose a colour")
|
|
44
|
+
}), /* @__PURE__ */ u("div", {
|
|
37
45
|
className: "flex flex-wrap gap-2",
|
|
38
|
-
children:
|
|
46
|
+
children: g.colours.map((e) => /* @__PURE__ */ u("button", {
|
|
39
47
|
type: "button",
|
|
40
|
-
"aria-pressed":
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
"aria-pressed": w === e,
|
|
49
|
+
disabled: m(j, S, e) === 0,
|
|
50
|
+
onClick: () => {
|
|
51
|
+
T(e), D(1);
|
|
52
|
+
},
|
|
53
|
+
className: t(h(w === e), "disabled:opacity-40 disabled:line-through"),
|
|
43
54
|
children: e
|
|
44
55
|
}, e))
|
|
45
56
|
})] }),
|
|
46
|
-
/* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ d("div", {
|
|
47
58
|
className: "flex items-center gap-3",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
59
|
+
children: [/* @__PURE__ */ u("label", {
|
|
49
60
|
htmlFor: "coo-add-quantity",
|
|
50
61
|
className: "text-sm text-text-secondary",
|
|
51
|
-
children:
|
|
52
|
-
}), /* @__PURE__ */
|
|
62
|
+
children: v("coolandlovely.cart.quantity", "Quantity")
|
|
63
|
+
}), /* @__PURE__ */ u("select", {
|
|
53
64
|
id: "coo-add-quantity",
|
|
54
|
-
value:
|
|
55
|
-
onChange: (e) =>
|
|
65
|
+
value: E,
|
|
66
|
+
onChange: (e) => D(Number(e.target.value)),
|
|
56
67
|
className: "rounded-lg border border-border-seam bg-bg-surface px-2 py-1.5 text-sm text-text-primary",
|
|
57
|
-
children: Array.from({ length:
|
|
68
|
+
children: Array.from({ length: P }, (e, t) => t + 1).map((e) => /* @__PURE__ */ u("option", {
|
|
58
69
|
value: e,
|
|
59
70
|
children: e
|
|
60
71
|
}, e))
|
|
61
72
|
})]
|
|
62
73
|
}),
|
|
63
|
-
|
|
74
|
+
M !== null && /* @__PURE__ */ u("p", {
|
|
75
|
+
"data-testid": "stock-left",
|
|
76
|
+
className: t("text-sm font-medium", N || M <= p ? "text-status-warning-text" : "text-text-secondary"),
|
|
77
|
+
children: N ? v("coolandlovely.cart.soldOut", "Sold out in this size and colour") : M <= p ? v("coolandlovely.cart.onlyLeft", "Only {{count}} left", { count: M }) : v("coolandlovely.cart.inStock", "In stock")
|
|
78
|
+
}),
|
|
79
|
+
b.error && /* @__PURE__ */ u("p", {
|
|
64
80
|
className: "text-sm text-status-error-text",
|
|
65
|
-
children:
|
|
81
|
+
children: b.error.message
|
|
66
82
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ d("div", {
|
|
68
84
|
className: "flex flex-wrap gap-2",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ d("button", {
|
|
70
86
|
type: "button",
|
|
71
|
-
disabled:
|
|
72
|
-
onClick: () =>
|
|
73
|
-
styleItemId:
|
|
74
|
-
quantity:
|
|
75
|
-
size:
|
|
76
|
-
colour:
|
|
77
|
-
lookId:
|
|
87
|
+
disabled: k || A || N || b.isPending,
|
|
88
|
+
onClick: () => b.mutate({ input: {
|
|
89
|
+
styleItemId: g.id,
|
|
90
|
+
quantity: E,
|
|
91
|
+
size: S,
|
|
92
|
+
colour: w,
|
|
93
|
+
lookId: _ ?? null
|
|
78
94
|
} }),
|
|
79
95
|
className: "inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
80
|
-
children: [
|
|
81
|
-
}),
|
|
96
|
+
children: [b.isPending ? /* @__PURE__ */ u(s, { className: "h-4 w-4 animate-spin" }) : b.isSuccess ? /* @__PURE__ */ u(o, { className: "h-4 w-4" }) : /* @__PURE__ */ u(c, { className: "h-4 w-4" }), b.isSuccess ? v("coolandlovely.cart.added", "Added to cart") : v("coolandlovely.cart.add", "Add to cart")]
|
|
97
|
+
}), b.isSuccess && /* @__PURE__ */ u("button", {
|
|
82
98
|
type: "button",
|
|
83
|
-
onClick: () =>
|
|
99
|
+
onClick: () => y("/cart"),
|
|
84
100
|
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
85
|
-
children:
|
|
101
|
+
children: v("coolandlovely.cart.viewCart", "View cart")
|
|
86
102
|
})]
|
|
87
103
|
})
|
|
88
104
|
]
|
|
89
|
-
}) : /* @__PURE__ */
|
|
105
|
+
}) : /* @__PURE__ */ u("p", {
|
|
90
106
|
className: "rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary",
|
|
91
|
-
children:
|
|
107
|
+
children: v("coolandlovely.cart.notForSale", "This piece is not available to buy right now.")
|
|
92
108
|
});
|
|
93
109
|
};
|
|
94
110
|
//#endregion
|
|
95
|
-
export {
|
|
111
|
+
export { g as AddToCartPanel };
|
|
96
112
|
|
|
97
113
|
//# sourceMappingURL=AddToCartPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddToCartPanel.js","names":[],"sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"sourcesContent":["/**\n * AddToCartPanel — size / colour / quantity picker + \"Add to cart\" for a catalogue piece.\n * @module coolandlovely/components\n *\n * When the shopper arrived from a look (\"shop the look\"), the look id rides along so the look's\n * creator earns the commission once the order is paid (BOFF-3370).\n */\n\nimport { type FC, useState } from 'react';\nimport { Check, Loader2, ShoppingBag, Sparkles } from 'lucide-react';\n\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useTr } from '../hooks';\nimport { useAddToCart } from '../hooks/useCommerceApi';\nimport type { CooStyleItem } from '../types';\nimport { cn } from '../utils/cn';\n\ninterface AddToCartPanelProps {\n item: Pick<CooStyleItem, 'id' | 'status' | 'price' | 'sizes' | 'colours'>;\n lookId?: string | null;\n}\n\nconst MAX_QUANTITY = 10;\n\nconst chip = (selected: boolean) =>\n cn(\n 'inline-flex min-w-9 items-center justify-center rounded-full border px-3 py-1 text-sm font-medium transition-colors',\n selected\n ? 'border-action-primary-bg bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam text-text-primary hover:bg-bg-sunken'\n );\n\nexport const AddToCartPanel: FC<AddToCartPanelProps> = ({ item, lookId }) => {\n const tr = useTr();\n const { navigateTo } = useCoolAndLovely();\n const addToCart = useAddToCart();\n const [size, setSize] = useState<string | null>(\n item.sizes.length === 1 ? (item.sizes[0] ?? null) : null\n );\n const [colour, setColour] = useState<string | null>(\n item.colours.length === 1 ? (item.colours[0] ?? null) : null\n );\n const [quantity, setQuantity] = useState(1);\n\n const purchasable = item.status === 'ACTIVE' && item.price > 0;\n const needsSize = item.sizes.length > 0 && !size;\n const needsColour = item.colours.length > 0 && !colour;\n\n if (!purchasable) {\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {tr('coolandlovely.cart.notForSale', 'This piece is not available to buy right now.')}\n </p>\n );\n }\n\n return (\n <div className=\"space-y-4\" data-testid=\"add-to-cart-panel\">\n {lookId && (\n <p className=\"inline-flex items-center gap-1.5 text-xs font-medium text-accent-primary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {tr(\n 'coolandlovely.cart.shoppingLook',\n 'Shopping from a look — your purchase supports its creator'\n )}\n </p>\n )}\n {item.sizes.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseSize', 'Choose a size')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.sizes.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={size === option}\n onClick={() => setSize(option)}\n className={chip(size === option)}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n {item.colours.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseColour', 'Choose a colour')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.colours.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={colour === option}\n onClick={() => setColour(option)}\n className={chip(colour === option)}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex items-center gap-3\">\n <label htmlFor=\"coo-add-quantity\" className=\"text-sm text-text-secondary\">\n {tr('coolandlovely.cart.quantity', 'Quantity')}\n </label>\n <select\n id=\"coo-add-quantity\"\n value={quantity}\n onChange={(event) => setQuantity(Number(event.target.value))}\n className=\"rounded-lg border border-border-seam bg-bg-surface px-2 py-1.5 text-sm text-text-primary\"\n >\n {Array.from({ length: MAX_QUANTITY }, (_, index) => index + 1).map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n </div>\n {addToCart.error && (\n <p className=\"text-sm text-status-error-text\">{addToCart.error.message}</p>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n disabled={needsSize || needsColour || addToCart.isPending}\n onClick={() =>\n addToCart.mutate({\n input: { styleItemId: item.id, quantity, size, colour, lookId: lookId ?? null },\n })\n }\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addToCart.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : addToCart.isSuccess ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <ShoppingBag className=\"h-4 w-4\" />\n )}\n {addToCart.isSuccess\n ? tr('coolandlovely.cart.added', 'Added to cart')\n : tr('coolandlovely.cart.add', 'Add to cart')}\n </button>\n {addToCart.isSuccess && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n {tr('coolandlovely.cart.viewCart', 'View cart')}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddToCartPanel.js","names":[],"sources":["../../../src/coolandlovely/components/AddToCartPanel.tsx"],"sourcesContent":["/**\n * AddToCartPanel — size / colour / quantity picker + \"Add to cart\" for a catalogue piece.\n * @module coolandlovely/components\n *\n * When the shopper arrived from a look (\"shop the look\"), the look id rides along so the look's\n * creator earns the commission once the order is paid (BOFF-3370).\n *\n * When the brand tracks stock for the piece (BOFF-9637), sold-out sizes and colours are disabled,\n * the quantity is capped at what is left, and a low stock count is shown. The service checks the\n * stock again when the piece is added and when the order is placed.\n */\n\nimport { type FC, useState } from 'react';\nimport { Check, Loader2, ShoppingBag, Sparkles } from 'lucide-react';\n\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useTr } from '../hooks';\nimport { useAddToCart } from '../hooks/useCommerceApi';\nimport { useStyleItemInventory } from '../hooks/useInventoryApi';\nimport type { CooStyleItem, CooStyleItemInventory } from '../types';\nimport { cn } from '../utils/cn';\n\ninterface AddToCartPanelProps {\n item: Pick<CooStyleItem, 'id' | 'status' | 'price' | 'sizes' | 'colours'>;\n lookId?: string | null;\n}\n\nconst MAX_QUANTITY = 10;\n/** Stock at or below this is shown to the shopper as \"Only N left\". */\nconst LOW_STOCK = 5;\n\n/**\n * Units left of a size/colour choice while the piece tracks stock; null when it does not (or the\n * stock could not be read — the service still enforces it). A partial choice (size picked, colour\n * not yet) sums every variant that matches it.\n */\nfunction unitsLeft(\n inventory: CooStyleItemInventory | null | undefined,\n size: string | null,\n colour: string | null\n): number | null {\n if (!inventory?.trackInventory) return null;\n return inventory.variants\n .filter(\n (variant) =>\n (size === null || (variant.size ?? '') === size) &&\n (colour === null || (variant.colour ?? '') === colour)\n )\n .reduce((sum, variant) => sum + variant.stockQuantity, 0);\n}\n\nconst chip = (selected: boolean) =>\n cn(\n 'inline-flex min-w-9 items-center justify-center rounded-full border px-3 py-1 text-sm font-medium transition-colors',\n selected\n ? 'border-action-primary-bg bg-action-primary-bg text-action-primary-text'\n : 'border-border-seam text-text-primary hover:bg-bg-sunken'\n );\n\nexport const AddToCartPanel: FC<AddToCartPanelProps> = ({ item, lookId }) => {\n const tr = useTr();\n const { navigateTo } = useCoolAndLovely();\n const addToCart = useAddToCart();\n const inventory = useStyleItemInventory(item.id);\n const [size, setSize] = useState<string | null>(\n item.sizes.length === 1 ? (item.sizes[0] ?? null) : null\n );\n const [colour, setColour] = useState<string | null>(\n item.colours.length === 1 ? (item.colours[0] ?? null) : null\n );\n const [quantity, setQuantity] = useState(1);\n\n const purchasable = item.status === 'ACTIVE' && item.price > 0;\n const needsSize = item.sizes.length > 0 && !size;\n const needsColour = item.colours.length > 0 && !colour;\n const stock = inventory.data;\n const left = needsSize || needsColour ? null : unitsLeft(stock, size ?? '', colour ?? '');\n const soldOut = left !== null && left <= 0;\n const maxQuantity = left === null ? MAX_QUANTITY : Math.max(1, Math.min(MAX_QUANTITY, left));\n\n if (!purchasable) {\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {tr('coolandlovely.cart.notForSale', 'This piece is not available to buy right now.')}\n </p>\n );\n }\n\n return (\n <div className=\"space-y-4\" data-testid=\"add-to-cart-panel\">\n {lookId && (\n <p className=\"inline-flex items-center gap-1.5 text-xs font-medium text-accent-primary\">\n <Sparkles className=\"h-3.5 w-3.5\" />\n {tr(\n 'coolandlovely.cart.shoppingLook',\n 'Shopping from a look — your purchase supports its creator'\n )}\n </p>\n )}\n {item.sizes.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseSize', 'Choose a size')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.sizes.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={size === option}\n disabled={unitsLeft(stock, option, colour) === 0}\n onClick={() => {\n setSize(option);\n setQuantity(1);\n }}\n className={cn(chip(size === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n {item.colours.length > 0 && (\n <div>\n <p className=\"mb-2 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {tr('coolandlovely.cart.chooseColour', 'Choose a colour')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {item.colours.map((option) => (\n <button\n key={option}\n type=\"button\"\n aria-pressed={colour === option}\n disabled={unitsLeft(stock, size, option) === 0}\n onClick={() => {\n setColour(option);\n setQuantity(1);\n }}\n className={cn(chip(colour === option), 'disabled:opacity-40 disabled:line-through')}\n >\n {option}\n </button>\n ))}\n </div>\n </div>\n )}\n <div className=\"flex items-center gap-3\">\n <label htmlFor=\"coo-add-quantity\" className=\"text-sm text-text-secondary\">\n {tr('coolandlovely.cart.quantity', 'Quantity')}\n </label>\n <select\n id=\"coo-add-quantity\"\n value={quantity}\n onChange={(event) => setQuantity(Number(event.target.value))}\n className=\"rounded-lg border border-border-seam bg-bg-surface px-2 py-1.5 text-sm text-text-primary\"\n >\n {Array.from({ length: maxQuantity }, (_, index) => index + 1).map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n </div>\n {left !== null && (\n <p\n data-testid=\"stock-left\"\n className={cn(\n 'text-sm font-medium',\n soldOut || left <= LOW_STOCK ? 'text-status-warning-text' : 'text-text-secondary'\n )}\n >\n {soldOut\n ? tr('coolandlovely.cart.soldOut', 'Sold out in this size and colour')\n : left <= LOW_STOCK\n ? tr('coolandlovely.cart.onlyLeft', 'Only {{count}} left', { count: left })\n : tr('coolandlovely.cart.inStock', 'In stock')}\n </p>\n )}\n {addToCart.error && (\n <p className=\"text-sm text-status-error-text\">{addToCart.error.message}</p>\n )}\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n disabled={needsSize || needsColour || soldOut || addToCart.isPending}\n onClick={() =>\n addToCart.mutate({\n input: { styleItemId: item.id, quantity, size, colour, lookId: lookId ?? null },\n })\n }\n className=\"inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {addToCart.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : addToCart.isSuccess ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <ShoppingBag className=\"h-4 w-4\" />\n )}\n {addToCart.isSuccess\n ? tr('coolandlovely.cart.added', 'Added to cart')\n : tr('coolandlovely.cart.add', 'Add to cart')}\n </button>\n {addToCart.isSuccess && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n {tr('coolandlovely.cart.viewCart', 'View cart')}\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CooCollaboration } from '../types';
|
|
3
|
+
|
|
4
|
+
interface CollaborationFeeCardProps {
|
|
5
|
+
collaboration: Pick<CooCollaboration, 'id' | 'title' | 'status' | 'creatorId' | 'compensation' | 'currency' | 'payout'>;
|
|
6
|
+
}
|
|
7
|
+
export declare const CollaborationFeeCard: FC<CollaborationFeeCardProps>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=CollaborationFeeCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollaborationFeeCard.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/CollaborationFeeCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,EAA4B,MAAM,OAAO,CAAC;AAO1D,OAAO,KAAK,EAAE,gBAAgB,EAAmC,MAAM,UAAU,CAAC;AAKlF,UAAU,yBAAyB;IACjC,aAAa,EAAE,IAAI,CACjB,gBAAgB,EAChB,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,QAAQ,CACjF,CAAC;CACH;AAOD,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC,yBAAyB,CA6I9D,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { useTr as t } from "../hooks/useTr.js";
|
|
3
|
+
import "../hooks/index.js";
|
|
4
|
+
import { StatusBadge as n } from "./StatusBadge.js";
|
|
5
|
+
import { formatDate as r, formatPrice as i } from "../utils/formatters.js";
|
|
6
|
+
import { usePayCollaborationFee as a, useSyncCollaborationPayment as o } from "../hooks/useCommerceApi.js";
|
|
7
|
+
import { PayForOrder as s } from "./PayForOrder.js";
|
|
8
|
+
import { useState as c } from "react";
|
|
9
|
+
import { CheckCircle2 as l, Loader2 as u, RefreshCw as d, Wallet as f } from "lucide-react";
|
|
10
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as h } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
//#region src/coolandlovely/components/CollaborationFeeCard.tsx
|
|
13
|
+
var g = "inline-flex w-full items-center justify-center gap-2 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50", _ = "inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50", v = ({ collaboration: v }) => {
|
|
14
|
+
let y = t(), { currentUser: b, navigateTo: x } = e(), S = a(), C = o(), [w, T] = c(null), E = v.payout ?? null, D = !!b?.id && b.id === v.creatorId;
|
|
15
|
+
if (!E && v.compensation <= 0) return null;
|
|
16
|
+
let O = async () => {
|
|
17
|
+
(await C.mutateAsync(v.id)).status === "PAID" && T(null);
|
|
18
|
+
}, k;
|
|
19
|
+
k = E ? E.status === "PAID" ? /* @__PURE__ */ m("div", {
|
|
20
|
+
className: "space-y-1 text-sm",
|
|
21
|
+
children: [/* @__PURE__ */ m("p", {
|
|
22
|
+
className: "inline-flex items-center gap-1.5 font-medium text-status-success-text",
|
|
23
|
+
children: [/* @__PURE__ */ p(l, { className: "h-4 w-4" }), y("coolandlovely.collaborationFee.paidOn", "Paid on {{date}}", { date: E.paidAt ? r(E.paidAt, {
|
|
24
|
+
hour: void 0,
|
|
25
|
+
minute: void 0
|
|
26
|
+
}) : "—" })]
|
|
27
|
+
}), D && /* @__PURE__ */ p("button", {
|
|
28
|
+
type: "button",
|
|
29
|
+
onClick: () => x("/earnings"),
|
|
30
|
+
className: "text-sm font-medium text-text-secondary hover:underline",
|
|
31
|
+
children: y("coolandlovely.collaborationFee.seeEarnings", "See it in your earnings")
|
|
32
|
+
})]
|
|
33
|
+
}) : D ? /* @__PURE__ */ p("p", {
|
|
34
|
+
className: "text-sm text-text-secondary",
|
|
35
|
+
children: y("coolandlovely.collaborationFee.awaitingBrand", "Waiting for the brand to pay your fee. It shows up in your earnings as soon as they do.")
|
|
36
|
+
}) : /* @__PURE__ */ m("div", {
|
|
37
|
+
className: "space-y-3",
|
|
38
|
+
"data-testid": "collaboration-fee-pay",
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ p("p", {
|
|
41
|
+
className: "text-sm text-text-secondary",
|
|
42
|
+
children: y("coolandlovely.collaborationFee.payHint", "This collaboration is published. Pay the agreed fee to the creator.")
|
|
43
|
+
}),
|
|
44
|
+
w ? /* @__PURE__ */ p(s, {
|
|
45
|
+
checkout: w,
|
|
46
|
+
onSettle: O,
|
|
47
|
+
description: y("coolandlovely.collaborationFee.paymentLabel", "Collaboration fee: {{title}}", { title: v.title }),
|
|
48
|
+
unavailableMessage: y("coolandlovely.collaborationFee.providerUnavailable", "Online payment is not available for this fee right now. Try again later.")
|
|
49
|
+
}) : /* @__PURE__ */ m("button", {
|
|
50
|
+
type: "button",
|
|
51
|
+
className: _,
|
|
52
|
+
disabled: S.isPending,
|
|
53
|
+
onClick: () => S.mutate(v.id, { onSuccess: T }),
|
|
54
|
+
children: [S.isPending ? /* @__PURE__ */ p(u, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ p(f, { className: "h-4 w-4" }), y("coolandlovely.collaborationFee.pay", "Pay fee")]
|
|
55
|
+
}),
|
|
56
|
+
E.paymentProvider && /* @__PURE__ */ m("button", {
|
|
57
|
+
type: "button",
|
|
58
|
+
className: g,
|
|
59
|
+
disabled: C.isPending,
|
|
60
|
+
onClick: () => void O(),
|
|
61
|
+
children: [C.isPending ? /* @__PURE__ */ p(u, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ p(d, { className: "h-4 w-4" }), y("coolandlovely.collaborationFee.checkPayment", "Check payment")]
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
}) : /* @__PURE__ */ p("p", {
|
|
65
|
+
className: "text-sm text-text-secondary",
|
|
66
|
+
children: v.status === "PUBLISHED" ? y("coolandlovely.collaborationFee.offPlatform", "No on-platform fee is due for this collaboration.") : y("coolandlovely.collaborationFee.dueOnPublish", "The agreed fee of {{amount}} becomes due from the brand when the collaboration is published.", { amount: i(v.compensation, v.currency) })
|
|
67
|
+
});
|
|
68
|
+
let A = S.error ?? C.error;
|
|
69
|
+
return /* @__PURE__ */ p("div", {
|
|
70
|
+
"data-testid": "collaboration-fee-card",
|
|
71
|
+
children: /* @__PURE__ */ m(h, {
|
|
72
|
+
treatment: "plain",
|
|
73
|
+
className: "space-y-3 p-5",
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ m("div", {
|
|
76
|
+
className: "flex items-center justify-between gap-2",
|
|
77
|
+
children: [/* @__PURE__ */ p("h3", {
|
|
78
|
+
className: "text-sm font-semibold text-text-primary",
|
|
79
|
+
children: y("coolandlovely.collaborationFee.title", "Collaboration fee")
|
|
80
|
+
}), E && /* @__PURE__ */ p(n, { status: E.status })]
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ p("p", {
|
|
83
|
+
className: "text-2xl font-semibold tabular-nums text-text-primary",
|
|
84
|
+
children: i(E?.amount ?? v.compensation, E?.currency ?? v.currency)
|
|
85
|
+
}),
|
|
86
|
+
k,
|
|
87
|
+
A && /* @__PURE__ */ p("p", {
|
|
88
|
+
className: "text-sm text-status-error-text",
|
|
89
|
+
children: A.message
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { v as CollaborationFeeCard };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=CollaborationFeeCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollaborationFeeCard.js","names":[],"sources":["../../../src/coolandlovely/components/CollaborationFeeCard.tsx"],"sourcesContent":["/**\n * CollaborationFeeCard — the agreed compensation of a collaboration and its payment (BOFF-9637).\n * @module coolandlovely/components\n *\n * Publishing a collaboration with an agreed compensation makes the fee due from the brand. The\n * brand owner pays it here through the provider global-billing chose; the fee only shows as paid\n * once the backend has re-read billing's payment record, and the creator then finds it on their\n * earnings page. The creator sees whether the brand has paid.\n */\n\nimport { type FC, type ReactNode, useState } from 'react';\nimport { CheckCircle2, Loader2, RefreshCw, Wallet } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\n\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useTr } from '../hooks';\nimport { usePayCollaborationFee, useSyncCollaborationPayment } from '../hooks/useCommerceApi';\nimport type { CooCollaboration, CooCollaborationPaymentCheckout } from '../types';\nimport { formatDate, formatPrice } from '../utils/formatters';\nimport { PayForOrder } from './PayForOrder';\nimport { StatusBadge } from './StatusBadge';\n\ninterface CollaborationFeeCardProps {\n collaboration: Pick<\n CooCollaboration,\n 'id' | 'title' | 'status' | 'creatorId' | 'compensation' | 'currency' | 'payout'\n >;\n}\n\nconst secondaryButton =\n 'inline-flex w-full items-center justify-center gap-2 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50';\nconst primaryButton =\n 'inline-flex w-full items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50';\n\nexport const CollaborationFeeCard: FC<CollaborationFeeCardProps> = ({ collaboration }) => {\n const tr = useTr();\n const { currentUser, navigateTo } = useCoolAndLovely();\n const pay = usePayCollaborationFee();\n const sync = useSyncCollaborationPayment();\n const [session, setSession] = useState<CooCollaborationPaymentCheckout | null>(null);\n\n const payout = collaboration.payout ?? null;\n const isCreator = !!currentUser?.id && currentUser.id === collaboration.creatorId;\n if (!payout && collaboration.compensation <= 0) return null;\n\n const settle = async (): Promise<void> => {\n const result = await sync.mutateAsync(collaboration.id);\n if (result.status === 'PAID') setSession(null);\n };\n\n let body: ReactNode;\n if (!payout) {\n body = (\n <p className=\"text-sm text-text-secondary\">\n {collaboration.status === 'PUBLISHED'\n ? tr(\n 'coolandlovely.collaborationFee.offPlatform',\n 'No on-platform fee is due for this collaboration.'\n )\n : tr(\n 'coolandlovely.collaborationFee.dueOnPublish',\n 'The agreed fee of {{amount}} becomes due from the brand when the collaboration is published.',\n { amount: formatPrice(collaboration.compensation, collaboration.currency) }\n )}\n </p>\n );\n } else if (payout.status === 'PAID') {\n body = (\n <div className=\"space-y-1 text-sm\">\n <p className=\"inline-flex items-center gap-1.5 font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-4 w-4\" />\n {tr('coolandlovely.collaborationFee.paidOn', 'Paid on {{date}}', {\n date: payout.paidAt\n ? formatDate(payout.paidAt, { hour: undefined, minute: undefined })\n : '—',\n })}\n </p>\n {isCreator && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/earnings')}\n className=\"text-sm font-medium text-text-secondary hover:underline\"\n >\n {tr('coolandlovely.collaborationFee.seeEarnings', 'See it in your earnings')}\n </button>\n )}\n </div>\n );\n } else if (isCreator) {\n body = (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.collaborationFee.awaitingBrand',\n 'Waiting for the brand to pay your fee. It shows up in your earnings as soon as they do.'\n )}\n </p>\n );\n } else {\n body = (\n <div className=\"space-y-3\" data-testid=\"collaboration-fee-pay\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.collaborationFee.payHint',\n 'This collaboration is published. Pay the agreed fee to the creator.'\n )}\n </p>\n {session ? (\n <PayForOrder\n checkout={session}\n onSettle={settle}\n description={tr(\n 'coolandlovely.collaborationFee.paymentLabel',\n 'Collaboration fee: {{title}}',\n { title: collaboration.title }\n )}\n unavailableMessage={tr(\n 'coolandlovely.collaborationFee.providerUnavailable',\n 'Online payment is not available for this fee right now. Try again later.'\n )}\n />\n ) : (\n <button\n type=\"button\"\n className={primaryButton}\n disabled={pay.isPending}\n onClick={() => pay.mutate(collaboration.id, { onSuccess: setSession })}\n >\n {pay.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Wallet className=\"h-4 w-4\" />\n )}\n {tr('coolandlovely.collaborationFee.pay', 'Pay fee')}\n </button>\n )}\n {payout.paymentProvider && (\n <button\n type=\"button\"\n className={secondaryButton}\n disabled={sync.isPending}\n onClick={() => void settle()}\n >\n {sync.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-4 w-4\" />\n )}\n {tr('coolandlovely.collaborationFee.checkPayment', 'Check payment')}\n </button>\n )}\n </div>\n );\n }\n\n const error = pay.error ?? sync.error;\n return (\n <div data-testid=\"collaboration-fee-card\">\n <GlassCard treatment=\"plain\" className=\"space-y-3 p-5\">\n <div className=\"flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr('coolandlovely.collaborationFee.title', 'Collaboration fee')}\n </h3>\n {payout && <StatusBadge status={payout.status} />}\n </div>\n <p className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {formatPrice(\n payout?.amount ?? collaboration.compensation,\n payout?.currency ?? collaboration.currency\n )}\n </p>\n {body}\n {error && <p className=\"text-sm text-status-error-text\">{error.message}</p>}\n </GlassCard>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { CooPaymentHandles } from '../types';
|
|
3
3
|
|
|
4
4
|
interface PayForOrderProps {
|
|
5
|
-
checkout:
|
|
6
|
-
|
|
5
|
+
checkout: CooPaymentHandles & {
|
|
6
|
+
order?: {
|
|
7
|
+
orderNumber: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
/** Ask the backend to settle the payment against billing; called after every attempt. */
|
|
7
11
|
onSettle: () => Promise<void>;
|
|
12
|
+
/** What the provider widget shows the payment is for (defaults to the order number). */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Shown when billing offered no provider this browser can use. */
|
|
15
|
+
unavailableMessage?: string;
|
|
8
16
|
}
|
|
9
17
|
export declare const PayForOrder: FC<PayForOrderProps>;
|
|
10
18
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayForOrder.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/PayForOrder.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"PayForOrder.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/PayForOrder.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,EAA+B,MAAM,OAAO,CAAC;AAG7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAUlD,UAAU,gBAAgB;IACxB,QAAQ,EAAE,iBAAiB,GAAG;QAAE,KAAK,CAAC,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAClE,yFAAyF;IACzF,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,wFAAwF;IACxF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAuGD,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAmE5C,CAAC"}
|
|
@@ -68,8 +68,8 @@ var m = ({ checkout: r, onSettle: s, publishableKey: m, clientSecret: h }) => {
|
|
|
68
68
|
})
|
|
69
69
|
]
|
|
70
70
|
});
|
|
71
|
-
}, h = ({ checkout: n, onSettle: i }) => {
|
|
72
|
-
let
|
|
71
|
+
}, h = ({ checkout: n, onSettle: i, description: a, unavailableMessage: l }) => {
|
|
72
|
+
let p = e(), [h, g] = o(!1), [_, v] = o(null);
|
|
73
73
|
if (n.clientSecret && n.stripePublishableKey) return /* @__PURE__ */ u(m, {
|
|
74
74
|
checkout: n,
|
|
75
75
|
onSettle: i,
|
|
@@ -77,39 +77,39 @@ var m = ({ checkout: r, onSettle: s, publishableKey: m, clientSecret: h }) => {
|
|
|
77
77
|
clientSecret: n.clientSecret
|
|
78
78
|
});
|
|
79
79
|
if (n.providerOrderId && n.razorpayKeyId) {
|
|
80
|
-
let { providerOrderId: e, razorpayKeyId: o } = n,
|
|
81
|
-
|
|
80
|
+
let { providerOrderId: e, razorpayKeyId: o } = n, l = async () => {
|
|
81
|
+
g(!0), v(null);
|
|
82
82
|
try {
|
|
83
83
|
await r({
|
|
84
84
|
keyId: o,
|
|
85
85
|
providerOrderId: e,
|
|
86
86
|
total: n.total,
|
|
87
87
|
currency: n.currency,
|
|
88
|
-
description: a("coolandlovely.checkout.orderLabel", "Order {{number}}", { number: n.order
|
|
88
|
+
description: a ?? p("coolandlovely.checkout.orderLabel", "Order {{number}}", { number: n.order?.orderNumber ?? "" })
|
|
89
89
|
}), await i();
|
|
90
90
|
} catch (e) {
|
|
91
|
-
|
|
91
|
+
v(e instanceof Error ? e.message : String(e));
|
|
92
92
|
} finally {
|
|
93
|
-
|
|
93
|
+
g(!1);
|
|
94
94
|
}
|
|
95
95
|
};
|
|
96
96
|
return /* @__PURE__ */ d("div", {
|
|
97
97
|
className: "space-y-3",
|
|
98
|
-
children: [
|
|
98
|
+
children: [_ && /* @__PURE__ */ u("p", {
|
|
99
99
|
className: "text-sm text-status-error-text",
|
|
100
|
-
children:
|
|
100
|
+
children: _
|
|
101
101
|
}), /* @__PURE__ */ d("button", {
|
|
102
102
|
type: "button",
|
|
103
103
|
className: f,
|
|
104
|
-
disabled:
|
|
105
|
-
onClick: () => void
|
|
106
|
-
children: [
|
|
104
|
+
disabled: h,
|
|
105
|
+
onClick: () => void l(),
|
|
106
|
+
children: [h ? /* @__PURE__ */ u(c, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ u(s, { className: "h-4 w-4" }), p("coolandlovely.checkout.payAmount", "Pay {{amount}}", { amount: t(n.total, n.currency) })]
|
|
107
107
|
})]
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
return /* @__PURE__ */ u("p", {
|
|
111
111
|
className: "rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary",
|
|
112
|
-
children:
|
|
112
|
+
children: l ?? p("coolandlovely.checkout.providerUnavailable", "Online payment is not available for this order right now. Your order is saved — try again from My Orders.")
|
|
113
113
|
});
|
|
114
114
|
};
|
|
115
115
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayForOrder.js","names":[],"sources":["../../../src/coolandlovely/components/PayForOrder.tsx"],"sourcesContent":["/**\n * PayForOrder — collects the payment for an order through the provider global-billing chose.\n * @module coolandlovely/components\n *\n * Razorpay: opens Razorpay's checkout widget for the billing-created order.\n * Stripe: mounts a Stripe card element and confirms the billing-issued client secret.\n * Whatever the provider reports, the order is only marked paid by `onSettle`, which asks the\n * backend to re-read billing's payment record (BOFF-3370).\n */\n\nimport { type FC, useEffect, useRef, useState } from 'react';\nimport { CreditCard, Loader2, Lock } from 'lucide-react';\n\nimport type { CooOrderCheckout } from '../types';\nimport { formatPrice } from '../utils/formatters';\nimport {\n type StripeCardElement,\n type StripeInstance,\n loadStripe,\n openRazorpayCheckout,\n} from '../utils/paymentProviders';\nimport { useTr } from '../hooks';\n\ninterface PayForOrderProps {\n checkout: CooOrderCheckout;\n /** Ask the backend to settle the order against billing; called after every attempt. */\n onSettle: () => Promise<void>;\n}\n\nconst primaryButton =\n 'inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50';\n\n/** Read a semantic colour token so the Stripe iframe matches the current theme. */\nfunction cssToken(name: string): string | undefined {\n const value = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return value || undefined;\n}\n\nconst StripeCardForm: FC<PayForOrderProps & { publishableKey: string; clientSecret: string }> = ({\n checkout,\n onSettle,\n publishableKey,\n clientSecret,\n}) => {\n const tr = useTr();\n const mountRef = useRef<HTMLDivElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n const cardRef = useRef<StripeCardElement | null>(null);\n const [ready, setReady] = useState(false);\n const [complete, setComplete] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n loadStripe(publishableKey)\n .then((stripe) => {\n if (cancelled || !mountRef.current) return;\n stripeRef.current = stripe;\n const card = stripe.elements().create('card', {\n style: {\n base: {\n fontSize: '16px',\n color: cssToken('--color-text-primary'),\n iconColor: cssToken('--color-text-secondary'),\n '::placeholder': { color: cssToken('--color-text-secondary') },\n },\n invalid: { color: cssToken('--color-status-error-text') },\n },\n });\n card.on('change', (event) => {\n setComplete(event.complete);\n setError(event.error?.message ?? null);\n });\n card.mount(mountRef.current);\n cardRef.current = card;\n setReady(true);\n })\n .catch((err: unknown) => {\n if (!cancelled) setError(err instanceof Error ? err.message : String(err));\n });\n return () => {\n cancelled = true;\n cardRef.current?.destroy();\n cardRef.current = null;\n };\n }, [publishableKey]);\n\n const pay = async () => {\n const stripe = stripeRef.current;\n const card = cardRef.current;\n if (!stripe || !card) return;\n setBusy(true);\n setError(null);\n try {\n const result = await stripe.confirmCardPayment(clientSecret, { payment_method: { card } });\n if (result.error)\n setError(\n result.error.message ??\n tr('coolandlovely.checkout.payFailed', 'The payment did not go through.')\n );\n await onSettle();\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <div\n ref={mountRef}\n data-testid=\"stripe-card-element\"\n className=\"rounded-lg border border-border-seam bg-bg-surface px-3 py-3\"\n />\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <button\n type=\"button\"\n className={primaryButton}\n disabled={!ready || !complete || busy}\n onClick={() => void pay()}\n >\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Lock className=\"h-4 w-4\" />}\n {tr('coolandlovely.checkout.payAmount', 'Pay {{amount}}', {\n amount: formatPrice(checkout.total, checkout.currency),\n })}\n </button>\n </div>\n );\n};\n\nexport const PayForOrder: FC<PayForOrderProps> = ({ checkout, onSettle }) => {\n const tr = useTr();\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (checkout.clientSecret && checkout.stripePublishableKey) {\n return (\n <StripeCardForm\n checkout={checkout}\n onSettle={onSettle}\n publishableKey={checkout.stripePublishableKey}\n clientSecret={checkout.clientSecret}\n />\n );\n }\n\n if (checkout.providerOrderId && checkout.razorpayKeyId) {\n const { providerOrderId, razorpayKeyId } = checkout;\n const pay = async () => {\n setBusy(true);\n setError(null);\n try {\n await openRazorpayCheckout({\n keyId: razorpayKeyId,\n providerOrderId,\n total: checkout.total,\n currency: checkout.currency,\n description: tr('coolandlovely.checkout.orderLabel', 'Order {{number}}', {\n number: checkout.order.orderNumber,\n }),\n });\n await onSettle();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusy(false);\n }\n };\n return (\n <div className=\"space-y-3\">\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <button type=\"button\" className={primaryButton} disabled={busy} onClick={() => void pay()}>\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <CreditCard className=\"h-4 w-4\" />}\n {tr('coolandlovely.checkout.payAmount', 'Pay {{amount}}', {\n amount: formatPrice(checkout.total, checkout.currency),\n })}\n </button>\n </div>\n );\n }\n\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.checkout.providerUnavailable',\n 'Online payment is not available for this order right now. Your order is saved — try again from My Orders.'\n )}\n </p>\n );\n};\n"],"mappings":";;;;;;;;AA6BA,IAAM,IACJ;AAGF,SAAS,EAAS,GAAkC;AAElD,QADc,OAAO,iBAAiB,SAAS,gBAAgB,CAAC,iBAAiB,EAAK,CAAC,MAAM,IAC7E,KAAA;;AAGlB,IAAM,KAA2F,EAC/F,aACA,aACA,mBACA,sBACI;CACJ,IAAM,IAAK,GAAO,EACZ,IAAW,EAA8B,KAAK,EAC9C,IAAY,EAA8B,KAAK,EAC/C,IAAU,EAAiC,KAAK,EAChD,CAAC,GAAO,KAAY,EAAS,GAAM,EACnC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAO,KAAY,EAAwB,KAAK;AAEvD,SAAgB;EACd,IAAI,IAAY;AA2BhB,SA1BA,EAAW,EAAe,CACvB,MAAM,MAAW;AAChB,OAAI,KAAa,CAAC,EAAS,QAAS;AACpC,KAAU,UAAU;GACpB,IAAM,IAAO,EAAO,UAAU,CAAC,OAAO,QAAQ,EAC5C,OAAO;IACL,MAAM;KACJ,UAAU;KACV,OAAO,EAAS,uBAAuB;KACvC,WAAW,EAAS,yBAAyB;KAC7C,iBAAiB,EAAE,OAAO,EAAS,yBAAyB,EAAE;KAC/D;IACD,SAAS,EAAE,OAAO,EAAS,4BAA4B,EAAE;IAC1D,EACF,CAAC;AAOF,GANA,EAAK,GAAG,WAAW,MAAU;AAE3B,IADA,EAAY,EAAM,SAAS,EAC3B,EAAS,EAAM,OAAO,WAAW,KAAK;KACtC,EACF,EAAK,MAAM,EAAS,QAAQ,EAC5B,EAAQ,UAAU,GAClB,EAAS,GAAK;IACd,CACD,OAAO,MAAiB;AACvB,GAAK,KAAW,EAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;IAC1E,QACS;AAGX,GAFA,IAAY,IACZ,EAAQ,SAAS,SAAS,EAC1B,EAAQ,UAAU;;IAEnB,CAAC,EAAe,CAAC;CAEpB,IAAM,IAAM,YAAY;EACtB,IAAM,IAAS,EAAU,SACnB,IAAO,EAAQ;AACjB,SAAC,KAAU,CAAC,IAEhB;GADA,EAAQ,GAAK,EACb,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAS,MAAM,EAAO,mBAAmB,GAAc,EAAE,gBAAgB,EAAE,SAAM,EAAE,CAAC;AAM1F,IALI,EAAO,SACT,EACE,EAAO,MAAM,WACX,EAAG,oCAAoC,kCAAkC,CAC5E,EACH,MAAM,GAAU;aACR;AACR,MAAQ,GAAM;;;;AAIlB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IACE,KAAK;IACL,eAAY;IACZ,WAAU;IACV,CAAA;GACD,KAAS,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAAU,CAAA;GACnE,kBAAC,UAAD;IACE,MAAK;IACL,WAAW;IACX,UAAU,CAAC,KAAS,CAAC,KAAY;IACjC,eAAe,KAAK,GAAK;cAJ3B,CAMG,IAAO,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAAG,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAClF,EAAG,oCAAoC,kBAAkB,EACxD,QAAQ,EAAY,EAAS,OAAO,EAAS,SAAS,EACvD,CAAC,CACK;;GACL;;GAIG,KAAqC,EAAE,aAAU,kBAAe;CAC3E,IAAM,IAAK,GAAO,EACZ,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAO,KAAY,EAAwB,KAAK;AAEvD,KAAI,EAAS,gBAAgB,EAAS,qBACpC,QACE,kBAAC,GAAD;EACY;EACA;EACV,gBAAgB,EAAS;EACzB,cAAc,EAAS;EACvB,CAAA;AAIN,KAAI,EAAS,mBAAmB,EAAS,eAAe;EACtD,IAAM,EAAE,oBAAiB,qBAAkB,GACrC,IAAM,YAAY;AAEtB,GADA,EAAQ,GAAK,EACb,EAAS,KAAK;AACd,OAAI;AAUF,IATA,MAAM,EAAqB;KACzB,OAAO;KACP;KACA,OAAO,EAAS;KAChB,UAAU,EAAS;KACnB,aAAa,EAAG,qCAAqC,oBAAoB,EACvE,QAAQ,EAAS,MAAM,aACxB,CAAC;KACH,CAAC,EACF,MAAM,GAAU;YACT,GAAK;AACZ,MAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;aAClD;AACR,MAAQ,GAAM;;;AAGlB,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KAAS,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAAU,CAAA,EACnE,kBAAC,UAAD;IAAQ,MAAK;IAAS,WAAW;IAAe,UAAU;IAAM,eAAe,KAAK,GAAK;cAAzF,CACG,IAAO,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAAG,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EACxF,EAAG,oCAAoC,kBAAkB,EACxD,QAAQ,EAAY,EAAS,OAAO,EAAS,SAAS,EACvD,CAAC,CACK;MACL;;;AAIV,QACE,kBAAC,KAAD;EAAG,WAAU;YACV,EACC,8CACA,4GACD;EACC,CAAA"}
|
|
1
|
+
{"version":3,"file":"PayForOrder.js","names":[],"sources":["../../../src/coolandlovely/components/PayForOrder.tsx"],"sourcesContent":["/**\n * PayForOrder — collects a payment through the provider global-billing chose: an order's payment\n * (BOFF-3370), or a brand's payment of a collaboration fee (BOFF-9637).\n * @module coolandlovely/components\n *\n * Razorpay: opens Razorpay's checkout widget for the billing-created order.\n * Stripe: mounts a Stripe card element and confirms the billing-issued client secret.\n * Whatever the provider reports, the order is only marked paid by `onSettle`, which asks the\n * backend to re-read billing's payment record (BOFF-3370).\n */\n\nimport { type FC, useEffect, useRef, useState } from 'react';\nimport { CreditCard, Loader2, Lock } from 'lucide-react';\n\nimport type { CooPaymentHandles } from '../types';\nimport { formatPrice } from '../utils/formatters';\nimport {\n type StripeCardElement,\n type StripeInstance,\n loadStripe,\n openRazorpayCheckout,\n} from '../utils/paymentProviders';\nimport { useTr } from '../hooks';\n\ninterface PayForOrderProps {\n checkout: CooPaymentHandles & { order?: { orderNumber: string } };\n /** Ask the backend to settle the payment against billing; called after every attempt. */\n onSettle: () => Promise<void>;\n /** What the provider widget shows the payment is for (defaults to the order number). */\n description?: string;\n /** Shown when billing offered no provider this browser can use. */\n unavailableMessage?: string;\n}\n\nconst primaryButton =\n 'inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2.5 text-sm font-semibold text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50';\n\n/** Read a semantic colour token so the Stripe iframe matches the current theme. */\nfunction cssToken(name: string): string | undefined {\n const value = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return value || undefined;\n}\n\nconst StripeCardForm: FC<PayForOrderProps & { publishableKey: string; clientSecret: string }> = ({\n checkout,\n onSettle,\n publishableKey,\n clientSecret,\n}) => {\n const tr = useTr();\n const mountRef = useRef<HTMLDivElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n const cardRef = useRef<StripeCardElement | null>(null);\n const [ready, setReady] = useState(false);\n const [complete, setComplete] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n loadStripe(publishableKey)\n .then((stripe) => {\n if (cancelled || !mountRef.current) return;\n stripeRef.current = stripe;\n const card = stripe.elements().create('card', {\n style: {\n base: {\n fontSize: '16px',\n color: cssToken('--color-text-primary'),\n iconColor: cssToken('--color-text-secondary'),\n '::placeholder': { color: cssToken('--color-text-secondary') },\n },\n invalid: { color: cssToken('--color-status-error-text') },\n },\n });\n card.on('change', (event) => {\n setComplete(event.complete);\n setError(event.error?.message ?? null);\n });\n card.mount(mountRef.current);\n cardRef.current = card;\n setReady(true);\n })\n .catch((err: unknown) => {\n if (!cancelled) setError(err instanceof Error ? err.message : String(err));\n });\n return () => {\n cancelled = true;\n cardRef.current?.destroy();\n cardRef.current = null;\n };\n }, [publishableKey]);\n\n const pay = async () => {\n const stripe = stripeRef.current;\n const card = cardRef.current;\n if (!stripe || !card) return;\n setBusy(true);\n setError(null);\n try {\n const result = await stripe.confirmCardPayment(clientSecret, { payment_method: { card } });\n if (result.error)\n setError(\n result.error.message ??\n tr('coolandlovely.checkout.payFailed', 'The payment did not go through.')\n );\n await onSettle();\n } finally {\n setBusy(false);\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <div\n ref={mountRef}\n data-testid=\"stripe-card-element\"\n className=\"rounded-lg border border-border-seam bg-bg-surface px-3 py-3\"\n />\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <button\n type=\"button\"\n className={primaryButton}\n disabled={!ready || !complete || busy}\n onClick={() => void pay()}\n >\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Lock className=\"h-4 w-4\" />}\n {tr('coolandlovely.checkout.payAmount', 'Pay {{amount}}', {\n amount: formatPrice(checkout.total, checkout.currency),\n })}\n </button>\n </div>\n );\n};\n\nexport const PayForOrder: FC<PayForOrderProps> = ({\n checkout,\n onSettle,\n description,\n unavailableMessage,\n}) => {\n const tr = useTr();\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n if (checkout.clientSecret && checkout.stripePublishableKey) {\n return (\n <StripeCardForm\n checkout={checkout}\n onSettle={onSettle}\n publishableKey={checkout.stripePublishableKey}\n clientSecret={checkout.clientSecret}\n />\n );\n }\n\n if (checkout.providerOrderId && checkout.razorpayKeyId) {\n const { providerOrderId, razorpayKeyId } = checkout;\n const pay = async () => {\n setBusy(true);\n setError(null);\n try {\n await openRazorpayCheckout({\n keyId: razorpayKeyId,\n providerOrderId,\n total: checkout.total,\n currency: checkout.currency,\n description:\n description ??\n tr('coolandlovely.checkout.orderLabel', 'Order {{number}}', {\n number: checkout.order?.orderNumber ?? '',\n }),\n });\n await onSettle();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setBusy(false);\n }\n };\n return (\n <div className=\"space-y-3\">\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n <button type=\"button\" className={primaryButton} disabled={busy} onClick={() => void pay()}>\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <CreditCard className=\"h-4 w-4\" />}\n {tr('coolandlovely.checkout.payAmount', 'Pay {{amount}}', {\n amount: formatPrice(checkout.total, checkout.currency),\n })}\n </button>\n </div>\n );\n }\n\n return (\n <p className=\"rounded-lg border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {unavailableMessage ??\n tr(\n 'coolandlovely.checkout.providerUnavailable',\n 'Online payment is not available for this order right now. Your order is saved — try again from My Orders.'\n )}\n </p>\n );\n};\n"],"mappings":";;;;;;;;AAkCA,IAAM,IACJ;AAGF,SAAS,EAAS,GAAkC;AAElD,QADc,OAAO,iBAAiB,SAAS,gBAAgB,CAAC,iBAAiB,EAAK,CAAC,MAAM,IAC7E,KAAA;;AAGlB,IAAM,KAA2F,EAC/F,aACA,aACA,mBACA,sBACI;CACJ,IAAM,IAAK,GAAO,EACZ,IAAW,EAA8B,KAAK,EAC9C,IAAY,EAA8B,KAAK,EAC/C,IAAU,EAAiC,KAAK,EAChD,CAAC,GAAO,KAAY,EAAS,GAAM,EACnC,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAO,KAAY,EAAwB,KAAK;AAEvD,SAAgB;EACd,IAAI,IAAY;AA2BhB,SA1BA,EAAW,EAAe,CACvB,MAAM,MAAW;AAChB,OAAI,KAAa,CAAC,EAAS,QAAS;AACpC,KAAU,UAAU;GACpB,IAAM,IAAO,EAAO,UAAU,CAAC,OAAO,QAAQ,EAC5C,OAAO;IACL,MAAM;KACJ,UAAU;KACV,OAAO,EAAS,uBAAuB;KACvC,WAAW,EAAS,yBAAyB;KAC7C,iBAAiB,EAAE,OAAO,EAAS,yBAAyB,EAAE;KAC/D;IACD,SAAS,EAAE,OAAO,EAAS,4BAA4B,EAAE;IAC1D,EACF,CAAC;AAOF,GANA,EAAK,GAAG,WAAW,MAAU;AAE3B,IADA,EAAY,EAAM,SAAS,EAC3B,EAAS,EAAM,OAAO,WAAW,KAAK;KACtC,EACF,EAAK,MAAM,EAAS,QAAQ,EAC5B,EAAQ,UAAU,GAClB,EAAS,GAAK;IACd,CACD,OAAO,MAAiB;AACvB,GAAK,KAAW,EAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;IAC1E,QACS;AAGX,GAFA,IAAY,IACZ,EAAQ,SAAS,SAAS,EAC1B,EAAQ,UAAU;;IAEnB,CAAC,EAAe,CAAC;CAEpB,IAAM,IAAM,YAAY;EACtB,IAAM,IAAS,EAAU,SACnB,IAAO,EAAQ;AACjB,SAAC,KAAU,CAAC,IAEhB;GADA,EAAQ,GAAK,EACb,EAAS,KAAK;AACd,OAAI;IACF,IAAM,IAAS,MAAM,EAAO,mBAAmB,GAAc,EAAE,gBAAgB,EAAE,SAAM,EAAE,CAAC;AAM1F,IALI,EAAO,SACT,EACE,EAAO,MAAM,WACX,EAAG,oCAAoC,kCAAkC,CAC5E,EACH,MAAM,GAAU;aACR;AACR,MAAQ,GAAM;;;;AAIlB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IACE,KAAK;IACL,eAAY;IACZ,WAAU;IACV,CAAA;GACD,KAAS,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAAU,CAAA;GACnE,kBAAC,UAAD;IACE,MAAK;IACL,WAAW;IACX,UAAU,CAAC,KAAS,CAAC,KAAY;IACjC,eAAe,KAAK,GAAK;cAJ3B,CAMG,IAAO,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAAG,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAClF,EAAG,oCAAoC,kBAAkB,EACxD,QAAQ,EAAY,EAAS,OAAO,EAAS,SAAS,EACvD,CAAC,CACK;;GACL;;GAIG,KAAqC,EAChD,aACA,aACA,gBACA,4BACI;CACJ,IAAM,IAAK,GAAO,EACZ,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAO,KAAY,EAAwB,KAAK;AAEvD,KAAI,EAAS,gBAAgB,EAAS,qBACpC,QACE,kBAAC,GAAD;EACY;EACA;EACV,gBAAgB,EAAS;EACzB,cAAc,EAAS;EACvB,CAAA;AAIN,KAAI,EAAS,mBAAmB,EAAS,eAAe;EACtD,IAAM,EAAE,oBAAiB,qBAAkB,GACrC,IAAM,YAAY;AAEtB,GADA,EAAQ,GAAK,EACb,EAAS,KAAK;AACd,OAAI;AAYF,IAXA,MAAM,EAAqB;KACzB,OAAO;KACP;KACA,OAAO,EAAS;KAChB,UAAU,EAAS;KACnB,aACE,KACA,EAAG,qCAAqC,oBAAoB,EAC1D,QAAQ,EAAS,OAAO,eAAe,IACxC,CAAC;KACL,CAAC,EACF,MAAM,GAAU;YACT,GAAK;AACZ,MAAS,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI,CAAC;aAClD;AACR,MAAQ,GAAM;;;AAGlB,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KAAS,kBAAC,KAAD;IAAG,WAAU;cAAkC;IAAU,CAAA,EACnE,kBAAC,UAAD;IAAQ,MAAK;IAAS,WAAW;IAAe,UAAU;IAAM,eAAe,KAAK,GAAK;cAAzF,CACG,IAAO,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAAG,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EACxF,EAAG,oCAAoC,kBAAkB,EACxD,QAAQ,EAAY,EAAS,OAAO,EAAS,SAAS,EACvD,CAAC,CACK;MACL;;;AAIV,QACE,kBAAC,KAAD;EAAG,WAAU;YACV,KACC,EACE,8CACA,4GACD;EACD,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CooBrandOrder } from '../types';
|
|
2
|
+
|
|
3
|
+
/** Whether the brand can (still) start or retry a refund of this order. */
|
|
4
|
+
export declare function canRefund(order: CooBrandOrder): boolean;
|
|
5
|
+
export declare function RefundPanel({ order }: {
|
|
6
|
+
order: CooBrandOrder;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=RefundPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RefundPanel.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/RefundPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAc9C,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAIvD;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,+BAwH9D"}
|