@anpayeras/agnostic-checkout 0.1.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 +439 -0
- package/dist/index.cjs +1622 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +321 -0
- package/dist/index.d.ts +321 -0
- package/dist/index.js +1564 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1622 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
Checkout: () => Checkout,
|
|
34
|
+
checkoutThemes: () => checkoutThemes,
|
|
35
|
+
cn: () => cn,
|
|
36
|
+
createCartEditPlugin: () => createCartEditPlugin,
|
|
37
|
+
createCssVars: () => createCssVars,
|
|
38
|
+
createDiscountPlugin: () => createDiscountPlugin,
|
|
39
|
+
darken: () => darken,
|
|
40
|
+
defaultTheme: () => defaultTheme,
|
|
41
|
+
formatNumber: () => formatNumber,
|
|
42
|
+
generateBrandTheme: () => generateBrandTheme,
|
|
43
|
+
getContrastColor: () => getContrastColor,
|
|
44
|
+
getLuminance: () => getLuminance,
|
|
45
|
+
hexToRgb: () => hexToRgb,
|
|
46
|
+
isDark: () => isDark,
|
|
47
|
+
isPaymentStep: () => isPaymentStep,
|
|
48
|
+
isReviewStep: () => isReviewStep,
|
|
49
|
+
lighten: () => lighten,
|
|
50
|
+
mergeTheme: () => mergeTheme,
|
|
51
|
+
mix: () => mix,
|
|
52
|
+
resolveTheme: () => resolveTheme,
|
|
53
|
+
rgbToHex: () => rgbToHex,
|
|
54
|
+
useCheckout: () => useCheckout
|
|
55
|
+
});
|
|
56
|
+
module.exports = __toCommonJS(index_exports);
|
|
57
|
+
|
|
58
|
+
// src/theme/color-utils.ts
|
|
59
|
+
function hexToRgb(hex) {
|
|
60
|
+
const clean = hex.replace("#", "");
|
|
61
|
+
const full = clean.length === 3 ? clean.split("").map((c) => c + c).join("") : clean;
|
|
62
|
+
return {
|
|
63
|
+
r: parseInt(full.slice(0, 2), 16),
|
|
64
|
+
g: parseInt(full.slice(2, 4), 16),
|
|
65
|
+
b: parseInt(full.slice(4, 6), 16)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function rgbToHex({ r, g, b }) {
|
|
69
|
+
const toHex = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
|
|
70
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
71
|
+
}
|
|
72
|
+
function getLuminance(hex) {
|
|
73
|
+
const { r, g, b } = hexToRgb(hex);
|
|
74
|
+
const [rs, gs, bs] = [r / 255, g / 255, b / 255].map(
|
|
75
|
+
(c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
|
76
|
+
);
|
|
77
|
+
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
78
|
+
}
|
|
79
|
+
function getContrastColor(hex) {
|
|
80
|
+
return getLuminance(hex) > 0.179 ? "#000000" : "#FFFFFF";
|
|
81
|
+
}
|
|
82
|
+
function isDark(hex) {
|
|
83
|
+
return getLuminance(hex) < 0.179;
|
|
84
|
+
}
|
|
85
|
+
function darken(hex, amount) {
|
|
86
|
+
const rgb = hexToRgb(hex);
|
|
87
|
+
const factor = 1 - amount / 100;
|
|
88
|
+
return rgbToHex({
|
|
89
|
+
r: rgb.r * factor,
|
|
90
|
+
g: rgb.g * factor,
|
|
91
|
+
b: rgb.b * factor
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function lighten(hex, amount) {
|
|
95
|
+
const rgb = hexToRgb(hex);
|
|
96
|
+
const factor = amount / 100;
|
|
97
|
+
return rgbToHex({
|
|
98
|
+
r: rgb.r + (255 - rgb.r) * factor,
|
|
99
|
+
g: rgb.g + (255 - rgb.g) * factor,
|
|
100
|
+
b: rgb.b + (255 - rgb.b) * factor
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function mix(hex1, hex2, ratio) {
|
|
104
|
+
const c1 = hexToRgb(hex1);
|
|
105
|
+
const c2 = hexToRgb(hex2);
|
|
106
|
+
return rgbToHex({
|
|
107
|
+
r: c1.r + (c2.r - c1.r) * ratio,
|
|
108
|
+
g: c1.g + (c2.g - c1.g) * ratio,
|
|
109
|
+
b: c1.b + (c2.b - c1.b) * ratio
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/theme/tokens.ts
|
|
114
|
+
var light = {
|
|
115
|
+
colors: {
|
|
116
|
+
primary: "#6366f1",
|
|
117
|
+
background: "#f8fafc",
|
|
118
|
+
surface: "#ffffff",
|
|
119
|
+
text: "#0f172a",
|
|
120
|
+
border: "#e2e8f0",
|
|
121
|
+
success: "#22c55e",
|
|
122
|
+
error: "#ef4444"
|
|
123
|
+
},
|
|
124
|
+
radius: "0.75rem",
|
|
125
|
+
fontFamily: "Inter, system-ui, sans-serif"
|
|
126
|
+
};
|
|
127
|
+
var dark = {
|
|
128
|
+
colors: {
|
|
129
|
+
primary: "#818cf8",
|
|
130
|
+
background: "#0f172a",
|
|
131
|
+
surface: "#1e293b",
|
|
132
|
+
text: "#f1f5f9",
|
|
133
|
+
border: "#334155",
|
|
134
|
+
success: "#34d399",
|
|
135
|
+
error: "#fb7185"
|
|
136
|
+
},
|
|
137
|
+
radius: "0.75rem",
|
|
138
|
+
fontFamily: "Inter, system-ui, sans-serif"
|
|
139
|
+
};
|
|
140
|
+
var minimal = {
|
|
141
|
+
colors: {
|
|
142
|
+
primary: "#0f172a",
|
|
143
|
+
background: "#ffffff",
|
|
144
|
+
surface: "#ffffff",
|
|
145
|
+
text: "#0f172a",
|
|
146
|
+
border: "#e5e7eb",
|
|
147
|
+
success: "#059669",
|
|
148
|
+
error: "#dc2626"
|
|
149
|
+
},
|
|
150
|
+
radius: "0.375rem",
|
|
151
|
+
fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif"
|
|
152
|
+
};
|
|
153
|
+
var corporate = {
|
|
154
|
+
colors: {
|
|
155
|
+
primary: "#1d4ed8",
|
|
156
|
+
background: "#f0f4f8",
|
|
157
|
+
surface: "#ffffff",
|
|
158
|
+
text: "#1e293b",
|
|
159
|
+
border: "#cbd5e1",
|
|
160
|
+
success: "#16a34a",
|
|
161
|
+
error: "#dc2626"
|
|
162
|
+
},
|
|
163
|
+
radius: "0.5rem",
|
|
164
|
+
fontFamily: "'Segoe UI', Roboto, 'Helvetica Neue', sans-serif"
|
|
165
|
+
};
|
|
166
|
+
var neon = {
|
|
167
|
+
colors: {
|
|
168
|
+
primary: "#a855f7",
|
|
169
|
+
background: "#0a0a0a",
|
|
170
|
+
surface: "#171717",
|
|
171
|
+
text: "#fafafa",
|
|
172
|
+
border: "#2e2e2e",
|
|
173
|
+
success: "#22d3ee",
|
|
174
|
+
error: "#f43f5e"
|
|
175
|
+
},
|
|
176
|
+
radius: "1rem",
|
|
177
|
+
fontFamily: "'Space Grotesk', system-ui, sans-serif"
|
|
178
|
+
};
|
|
179
|
+
var checkoutThemes = {
|
|
180
|
+
light,
|
|
181
|
+
dark,
|
|
182
|
+
minimal,
|
|
183
|
+
corporate,
|
|
184
|
+
neon
|
|
185
|
+
};
|
|
186
|
+
var defaultTheme = light;
|
|
187
|
+
function mergeTheme(base, overrides) {
|
|
188
|
+
if (!overrides) return base;
|
|
189
|
+
return {
|
|
190
|
+
colors: { ...base.colors, ...overrides.colors },
|
|
191
|
+
radius: overrides.radius ?? base.radius,
|
|
192
|
+
fontFamily: overrides.fontFamily ?? base.fontFamily
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function createCssVars(theme) {
|
|
196
|
+
return {
|
|
197
|
+
"--color-primary": theme.colors.primary,
|
|
198
|
+
"--color-background": theme.colors.background,
|
|
199
|
+
"--color-surface": theme.colors.surface,
|
|
200
|
+
"--color-text": theme.colors.text,
|
|
201
|
+
"--color-border": theme.colors.border,
|
|
202
|
+
"--color-success": theme.colors.success,
|
|
203
|
+
"--color-error": theme.colors.error,
|
|
204
|
+
"--theme-radius": theme.radius,
|
|
205
|
+
"--theme-font": theme.fontFamily
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function generateBrandTheme(brandColor) {
|
|
209
|
+
const background = "#FFFFFF";
|
|
210
|
+
const text = getContrastColor(background);
|
|
211
|
+
return {
|
|
212
|
+
colors: {
|
|
213
|
+
primary: brandColor,
|
|
214
|
+
background,
|
|
215
|
+
surface: "#F9FAFB",
|
|
216
|
+
text,
|
|
217
|
+
border: "#E5E7EB",
|
|
218
|
+
success: "#16A34A",
|
|
219
|
+
error: "#DC2626"
|
|
220
|
+
},
|
|
221
|
+
radius: "0.75rem",
|
|
222
|
+
fontFamily: "Inter, system-ui, sans-serif"
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function resolveTheme(theme, brandColor, customTheme) {
|
|
226
|
+
let base;
|
|
227
|
+
if (brandColor) {
|
|
228
|
+
base = generateBrandTheme(brandColor);
|
|
229
|
+
} else if (typeof theme === "string") {
|
|
230
|
+
base = checkoutThemes[theme] ?? defaultTheme;
|
|
231
|
+
} else {
|
|
232
|
+
base = mergeTheme(defaultTheme, theme);
|
|
233
|
+
}
|
|
234
|
+
return mergeTheme(base, customTheme);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/plugins/discount-plugin.tsx
|
|
238
|
+
var import_react = require("react");
|
|
239
|
+
var import_lucide_react = require("lucide-react");
|
|
240
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
241
|
+
var CouponInput = ({ onApply, t, compact }) => {
|
|
242
|
+
const [code, setCode] = (0, import_react.useState)("");
|
|
243
|
+
const [status, setStatus] = (0, import_react.useState)("idle");
|
|
244
|
+
const handleApply = () => {
|
|
245
|
+
if (!code.trim()) return;
|
|
246
|
+
setStatus("loading");
|
|
247
|
+
onApply(code.trim());
|
|
248
|
+
setCode("");
|
|
249
|
+
setTimeout(() => setStatus("idle"), 500);
|
|
250
|
+
};
|
|
251
|
+
const isBtnDisabled = status === "loading" || !code.trim();
|
|
252
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 flex items-center gap-2 border border-border/60 focus-within:border-primary/50 focus-within:ring-2 focus-within:ring-primary/5 rounded-xs px-3 py-2 transition-all bg-white", children: [
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Tag, { className: `${compact ? "w-3 h-3" : "w-3.5 h-3.5"} text-text/30 shrink-0` }),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
256
|
+
"input",
|
|
257
|
+
{
|
|
258
|
+
type: "text",
|
|
259
|
+
value: code,
|
|
260
|
+
onChange: (e) => setCode(e.target.value),
|
|
261
|
+
onKeyDown: (e) => e.key === "Enter" && handleApply(),
|
|
262
|
+
placeholder: t.couponPlaceholder,
|
|
263
|
+
className: `bg-transparent outline-none w-full placeholder:text-text/30 text-text font-semibold ${compact ? "text-xs" : "text-sm"}`
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
] }),
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
268
|
+
"button",
|
|
269
|
+
{
|
|
270
|
+
type: "button",
|
|
271
|
+
onClick: handleApply,
|
|
272
|
+
disabled: isBtnDisabled,
|
|
273
|
+
className: `font-bold bg-primary text-surface rounded-xs hover:brightness-110 disabled:opacity-40 transition-all ${compact ? "px-3 py-2 text-xs" : "px-4 py-2 text-sm"} ${isBtnDisabled ? "cursor-not-allowed" : "cursor-pointer"}`,
|
|
274
|
+
children: status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "w-3.5 h-3.5 animate-spin" }) : t.couponApply
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
] });
|
|
278
|
+
};
|
|
279
|
+
var RemoveCouponButton = ({ onRemove, t }) => {
|
|
280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
281
|
+
"button",
|
|
282
|
+
{
|
|
283
|
+
type: "button",
|
|
284
|
+
onClick: onRemove,
|
|
285
|
+
className: "p-0.5 rounded text-error hover:text-error hover:bg-error/10 transition-colors",
|
|
286
|
+
"aria-label": t.couponRemove,
|
|
287
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Trash2, { className: "w-3 h-3" })
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
};
|
|
291
|
+
function createDiscountPlugin(emit) {
|
|
292
|
+
return {
|
|
293
|
+
id: "discount",
|
|
294
|
+
slots: {
|
|
295
|
+
sidebarBeforeTotals: ({ step, t }) => {
|
|
296
|
+
if (step !== "review") return null;
|
|
297
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
298
|
+
CouponInput,
|
|
299
|
+
{
|
|
300
|
+
onApply: (code) => emit({ type: "COUPON_APPLY_REQUESTED", code }),
|
|
301
|
+
t,
|
|
302
|
+
compact: true
|
|
303
|
+
}
|
|
304
|
+
) });
|
|
305
|
+
},
|
|
306
|
+
mobileBarExpanded: ({ step, t }) => {
|
|
307
|
+
if (step !== "review") return null;
|
|
308
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-3 pb-3 border-b border-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
309
|
+
CouponInput,
|
|
310
|
+
{
|
|
311
|
+
onApply: (code) => emit({ type: "COUPON_APPLY_REQUESTED", code }),
|
|
312
|
+
t
|
|
313
|
+
}
|
|
314
|
+
) });
|
|
315
|
+
},
|
|
316
|
+
totalsDiscountAction: ({ step, t }) => {
|
|
317
|
+
if (step !== "review") return null;
|
|
318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
319
|
+
RemoveCouponButton,
|
|
320
|
+
{
|
|
321
|
+
onRemove: () => emit({ type: "COUPON_REMOVE_REQUESTED" }),
|
|
322
|
+
t
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/plugins/cart-edit-plugin.tsx
|
|
331
|
+
var import_react2 = require("react");
|
|
332
|
+
var import_lucide_react2 = require("lucide-react");
|
|
333
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
334
|
+
var QuantityStepper = ({ quantity, min, max, loading, onChange }) => {
|
|
335
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
336
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center border border-border/60 rounded-xl bg-background/30 p-0.5 overflow-hidden", children: [
|
|
337
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
338
|
+
"button",
|
|
339
|
+
{
|
|
340
|
+
type: "button",
|
|
341
|
+
onClick: () => onChange(quantity - 1),
|
|
342
|
+
disabled: loading || quantity <= min,
|
|
343
|
+
className: "w-8 h-8 flex items-center justify-center text-text/60 hover:bg-border/30 rounded-lg transition-colors disabled:opacity-30",
|
|
344
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Minus, { className: "w-3.5 h-3.5" })
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm font-bold w-7 text-center select-none", children: quantity }),
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
349
|
+
"button",
|
|
350
|
+
{
|
|
351
|
+
type: "button",
|
|
352
|
+
onClick: () => onChange(quantity + 1),
|
|
353
|
+
disabled: loading || quantity >= max,
|
|
354
|
+
className: "w-8 h-8 flex items-center justify-center text-text/60 hover:bg-border/30 rounded-lg transition-colors disabled:opacity-30",
|
|
355
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Plus, { className: "w-3.5 h-3.5" })
|
|
356
|
+
}
|
|
357
|
+
)
|
|
358
|
+
] }),
|
|
359
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Loader2, { className: "w-4 h-4 animate-spin text-primary" })
|
|
360
|
+
] });
|
|
361
|
+
};
|
|
362
|
+
var RemoveButton = ({ loading, onRemove, label }) => {
|
|
363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
364
|
+
"button",
|
|
365
|
+
{
|
|
366
|
+
type: "button",
|
|
367
|
+
onClick: onRemove,
|
|
368
|
+
disabled: loading,
|
|
369
|
+
className: "p-2 -mr-1 rounded-full text-text/20 hover:text-error hover:bg-error/10 transition-all disabled:opacity-50",
|
|
370
|
+
"aria-label": label,
|
|
371
|
+
children: loading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Loader2, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Trash2, { className: "w-5 h-5" })
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
};
|
|
375
|
+
var CartEditItemActions = ({
|
|
376
|
+
props,
|
|
377
|
+
emit,
|
|
378
|
+
options
|
|
379
|
+
}) => {
|
|
380
|
+
const [loading, setLoading] = (0, import_react2.useState)(false);
|
|
381
|
+
const { item, t } = props;
|
|
382
|
+
const handleQtyChange = (qty) => {
|
|
383
|
+
if (qty < options.minQuantity || qty > options.maxQuantity) return;
|
|
384
|
+
setLoading(true);
|
|
385
|
+
emit({ type: "ITEM_QUANTITY_UPDATE_REQUESTED", itemId: item.id, quantity: qty });
|
|
386
|
+
setTimeout(() => setLoading(false), 2e3);
|
|
387
|
+
};
|
|
388
|
+
if (!options.allowQuantityEdit) {
|
|
389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-sm text-text/40 mt-1 block font-semibold uppercase tracking-wider", children: [
|
|
390
|
+
t.quantityShort,
|
|
391
|
+
": ",
|
|
392
|
+
item.quantity
|
|
393
|
+
] });
|
|
394
|
+
}
|
|
395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
396
|
+
QuantityStepper,
|
|
397
|
+
{
|
|
398
|
+
quantity: item.quantity,
|
|
399
|
+
min: options.minQuantity,
|
|
400
|
+
max: options.maxQuantity,
|
|
401
|
+
loading,
|
|
402
|
+
onChange: handleQtyChange
|
|
403
|
+
}
|
|
404
|
+
) });
|
|
405
|
+
};
|
|
406
|
+
var CartEditItemEnd = ({
|
|
407
|
+
props,
|
|
408
|
+
emit,
|
|
409
|
+
options
|
|
410
|
+
}) => {
|
|
411
|
+
const [loading, setLoading] = (0, import_react2.useState)(false);
|
|
412
|
+
const { item, t } = props;
|
|
413
|
+
if (!options.allowRemove) return null;
|
|
414
|
+
const handleRemove = () => {
|
|
415
|
+
setLoading(true);
|
|
416
|
+
emit({ type: "ITEM_REMOVE_REQUESTED", itemId: item.id });
|
|
417
|
+
setTimeout(() => setLoading(false), 2e3);
|
|
418
|
+
};
|
|
419
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
420
|
+
RemoveButton,
|
|
421
|
+
{
|
|
422
|
+
loading,
|
|
423
|
+
onRemove: handleRemove,
|
|
424
|
+
label: t.removeItem
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
};
|
|
428
|
+
function createCartEditPlugin(emit, options) {
|
|
429
|
+
const opts = {
|
|
430
|
+
allowRemove: !!options?.allowRemove,
|
|
431
|
+
allowQuantityEdit: !!options?.allowQuantityEdit,
|
|
432
|
+
minQuantity: options?.minQuantity ?? 1,
|
|
433
|
+
maxQuantity: options?.maxQuantity ?? 99
|
|
434
|
+
};
|
|
435
|
+
return {
|
|
436
|
+
id: "cart-edit",
|
|
437
|
+
slots: {
|
|
438
|
+
orderItemActions: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CartEditItemActions, { props, emit, options: opts }),
|
|
439
|
+
orderItemEnd: (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CartEditItemEnd, { props, emit, options: opts })
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/core/checkout-context.tsx
|
|
445
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
446
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
447
|
+
var CheckoutContext = (0, import_react3.createContext)(null);
|
|
448
|
+
var checkoutReducer = (state, action) => {
|
|
449
|
+
switch (action.type) {
|
|
450
|
+
case "SET_STEP":
|
|
451
|
+
return { ...state, step: action.step };
|
|
452
|
+
case "SELECT_METHOD":
|
|
453
|
+
return { ...state, selectedMethodId: action.methodId };
|
|
454
|
+
case "SET_ERROR":
|
|
455
|
+
return { ...state, error: action.error };
|
|
456
|
+
case "SET_CART":
|
|
457
|
+
return { ...state, items: action.cartState.items, totals: action.cartState.totals };
|
|
458
|
+
case "SYNC_PROPS":
|
|
459
|
+
return { ...state, items: action.items, totals: action.totals };
|
|
460
|
+
case "SET_PAYMENT_RESULT":
|
|
461
|
+
return { ...state, paymentResult: action.result };
|
|
462
|
+
default:
|
|
463
|
+
return state;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var CheckoutProvider = ({
|
|
467
|
+
children,
|
|
468
|
+
config,
|
|
469
|
+
initialStatus,
|
|
470
|
+
onEvent
|
|
471
|
+
}) => {
|
|
472
|
+
const [state, dispatch] = (0, import_react3.useReducer)(checkoutReducer, {
|
|
473
|
+
...config,
|
|
474
|
+
step: initialStatus ? initialStatus.status : "review",
|
|
475
|
+
selectedMethodId: null,
|
|
476
|
+
error: null,
|
|
477
|
+
paymentResult: initialStatus
|
|
478
|
+
});
|
|
479
|
+
(0, import_react3.useEffect)(() => {
|
|
480
|
+
dispatch({ type: "SYNC_PROPS", items: config.items, totals: config.totals });
|
|
481
|
+
}, [config.items, config.totals]);
|
|
482
|
+
const [eventLog, setEventLog] = import_react3.default.useState([]);
|
|
483
|
+
const emitEvent = (0, import_react3.useCallback)(
|
|
484
|
+
(event) => {
|
|
485
|
+
onEvent?.(event);
|
|
486
|
+
setEventLog(
|
|
487
|
+
(prev) => [`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${event.type}`, ...prev].slice(0, 20)
|
|
488
|
+
);
|
|
489
|
+
},
|
|
490
|
+
[onEvent]
|
|
491
|
+
);
|
|
492
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckoutContext.Provider, { value: { state, dispatch, emitEvent, eventLog }, children });
|
|
493
|
+
};
|
|
494
|
+
var useCheckoutContext = () => {
|
|
495
|
+
const context = (0, import_react3.useContext)(CheckoutContext);
|
|
496
|
+
if (!context) {
|
|
497
|
+
throw new Error("useCheckoutContext must be used within a CheckoutProvider");
|
|
498
|
+
}
|
|
499
|
+
return context;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/hooks/useCheckout.ts
|
|
503
|
+
var useCheckout = () => {
|
|
504
|
+
const { state, dispatch, emitEvent } = useCheckoutContext();
|
|
505
|
+
const setStep = (step) => {
|
|
506
|
+
dispatch({ type: "SET_STEP", step });
|
|
507
|
+
emitEvent({ type: "STEP_CHANGED", step });
|
|
508
|
+
};
|
|
509
|
+
const selectMethod = (methodId) => {
|
|
510
|
+
dispatch({ type: "SELECT_METHOD", methodId });
|
|
511
|
+
emitEvent({ type: "PAYMENT_METHOD_SELECTED", methodId });
|
|
512
|
+
};
|
|
513
|
+
const submitPayment = async (data) => {
|
|
514
|
+
if (!state.selectedMethodId) return;
|
|
515
|
+
emitEvent({ type: "PAYMENT_SUBMITTED" });
|
|
516
|
+
setStep("processing");
|
|
517
|
+
dispatch({ type: "SET_ERROR", error: null });
|
|
518
|
+
try {
|
|
519
|
+
const result = await state.provider.createPayment(data);
|
|
520
|
+
dispatch({ type: "SET_PAYMENT_RESULT", result });
|
|
521
|
+
console.log(result);
|
|
522
|
+
switch (result.status) {
|
|
523
|
+
case "success":
|
|
524
|
+
emitEvent({ type: "PAYMENT_SUCCESS", transactionId: "unknown" });
|
|
525
|
+
setStep("success");
|
|
526
|
+
break;
|
|
527
|
+
case "pending":
|
|
528
|
+
emitEvent({ type: "STEP_CHANGED", step: "pending" });
|
|
529
|
+
setStep("pending");
|
|
530
|
+
break;
|
|
531
|
+
case "redirect":
|
|
532
|
+
emitEvent({ type: "STEP_CHANGED", step: "redirect" });
|
|
533
|
+
setStep("redirect");
|
|
534
|
+
break;
|
|
535
|
+
case "error": {
|
|
536
|
+
const errMsg = result.feedback?.description ?? "Payment failed";
|
|
537
|
+
dispatch({ type: "SET_ERROR", error: errMsg });
|
|
538
|
+
emitEvent({ type: "PAYMENT_ERROR", error: errMsg });
|
|
539
|
+
setStep("error");
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} catch (error) {
|
|
544
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
545
|
+
dispatch({ type: "SET_ERROR", error: message });
|
|
546
|
+
emitEvent({ type: "PAYMENT_ERROR", error: message });
|
|
547
|
+
setStep("error");
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
const setError = (error) => {
|
|
551
|
+
dispatch({ type: "SET_ERROR", error });
|
|
552
|
+
emitEvent({ type: "PAYMENT_ERROR", error });
|
|
553
|
+
};
|
|
554
|
+
const updateQuantity = async (id, qty) => {
|
|
555
|
+
if (!state.cartController) return;
|
|
556
|
+
const cartState = await state.cartController.updateQuantity(id, qty);
|
|
557
|
+
dispatch({ type: "SET_CART", cartState });
|
|
558
|
+
};
|
|
559
|
+
const removeItem = async (id) => {
|
|
560
|
+
if (!state.cartController) return;
|
|
561
|
+
const cartState = await state.cartController.removeItem(id);
|
|
562
|
+
dispatch({ type: "SET_CART", cartState });
|
|
563
|
+
};
|
|
564
|
+
const applyCoupon = async (code) => {
|
|
565
|
+
if (!state.cartController) return false;
|
|
566
|
+
try {
|
|
567
|
+
const cartState = await state.cartController.applyCoupon(code);
|
|
568
|
+
dispatch({ type: "SET_CART", cartState });
|
|
569
|
+
return true;
|
|
570
|
+
} catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const removeCoupon = async () => {
|
|
575
|
+
if (!state.cartController?.removeCoupon) return false;
|
|
576
|
+
try {
|
|
577
|
+
const cartState = await state.cartController.removeCoupon();
|
|
578
|
+
dispatch({ type: "SET_CART", cartState });
|
|
579
|
+
return true;
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
return {
|
|
585
|
+
...state,
|
|
586
|
+
setStep,
|
|
587
|
+
selectMethod,
|
|
588
|
+
submitPayment,
|
|
589
|
+
setError,
|
|
590
|
+
updateQuantity,
|
|
591
|
+
removeItem,
|
|
592
|
+
applyCoupon,
|
|
593
|
+
removeCoupon,
|
|
594
|
+
hasCartController: !!state.cartController,
|
|
595
|
+
hasRemoveCoupon: !!state.cartController?.removeCoupon
|
|
596
|
+
};
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
// src/utils/utils.ts
|
|
600
|
+
var import_clsx = require("clsx");
|
|
601
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
602
|
+
function cn(...inputs) {
|
|
603
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
604
|
+
}
|
|
605
|
+
var isPaymentStep = (step) => step === "payment_method" || step === "payment_details";
|
|
606
|
+
var isReviewStep = (step) => step === "review";
|
|
607
|
+
var formatNumber = (n, currency) => new Intl.NumberFormat("en-US", { style: "currency", currency }).format(n);
|
|
608
|
+
|
|
609
|
+
// src/components/Checkout.tsx
|
|
610
|
+
var import_react11 = require("react");
|
|
611
|
+
|
|
612
|
+
// src/theme/theme-provider.tsx
|
|
613
|
+
var import_react4 = require("react");
|
|
614
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
615
|
+
var ThemeProvider = ({ theme, brandColor, customTheme, children }) => {
|
|
616
|
+
const resolved = resolveTheme(theme, brandColor, customTheme);
|
|
617
|
+
const cssVars = createCssVars(resolved);
|
|
618
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
619
|
+
"div",
|
|
620
|
+
{
|
|
621
|
+
style: cssVars,
|
|
622
|
+
className: "bg-background text-text font-(--theme-font) h-full w-full",
|
|
623
|
+
children
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// src/core/i18n-context.tsx
|
|
629
|
+
var import_react5 = require("react");
|
|
630
|
+
|
|
631
|
+
// src/locales/en.ts
|
|
632
|
+
var en = {
|
|
633
|
+
// Order Summary
|
|
634
|
+
orderSummary: "Order Summary",
|
|
635
|
+
quantity: "Qty",
|
|
636
|
+
quantityShort: "Qty",
|
|
637
|
+
removeItem: "Remove item",
|
|
638
|
+
// Totals
|
|
639
|
+
subtotal: "Subtotal",
|
|
640
|
+
discount: "Discount",
|
|
641
|
+
shipping: "Shipping",
|
|
642
|
+
shippingFree: "Free",
|
|
643
|
+
taxes: "Taxes",
|
|
644
|
+
total: "Total",
|
|
645
|
+
// Coupon
|
|
646
|
+
couponPlaceholder: "Promo code",
|
|
647
|
+
couponApply: "Apply",
|
|
648
|
+
couponApplied: "\u2713 Coupon applied",
|
|
649
|
+
couponInvalid: "\u2717 Invalid code",
|
|
650
|
+
couponRemove: "Remove coupon",
|
|
651
|
+
// Sidebar
|
|
652
|
+
orderSummaryTitle: "Order summary",
|
|
653
|
+
securePayment: "SECURE SSL PAYMENT",
|
|
654
|
+
// Actions
|
|
655
|
+
continueToPay: "Continue to payment",
|
|
656
|
+
payNow: "Pay now",
|
|
657
|
+
pay: "Pay",
|
|
658
|
+
processing: "Processing\u2026",
|
|
659
|
+
continueNow: "Continue now",
|
|
660
|
+
// Payment Methods
|
|
661
|
+
paymentMethod: "Payment method",
|
|
662
|
+
back: "Back",
|
|
663
|
+
completeDetails: "Complete details",
|
|
664
|
+
selectMethod: "Select a method",
|
|
665
|
+
// Processing
|
|
666
|
+
processingPayment: "Processing payment\u2026",
|
|
667
|
+
processingDescription: "Don't close or reload this window.",
|
|
668
|
+
// Redirect
|
|
669
|
+
redirecting: "Redirecting\u2026",
|
|
670
|
+
redirectDescription: "You will be redirected to complete your payment securely.",
|
|
671
|
+
// Mobile bar
|
|
672
|
+
showOrderDetails: "Show order details",
|
|
673
|
+
hideOrderDetails: "Hide order details",
|
|
674
|
+
continue: "Continue"
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
// src/locales/es.ts
|
|
678
|
+
var es = {
|
|
679
|
+
// Order Summary
|
|
680
|
+
orderSummary: "Resumen del pedido",
|
|
681
|
+
quantity: "Cant",
|
|
682
|
+
quantityShort: "Cant",
|
|
683
|
+
removeItem: "Eliminar item",
|
|
684
|
+
// Totals
|
|
685
|
+
subtotal: "Subtotal",
|
|
686
|
+
discount: "Descuento",
|
|
687
|
+
shipping: "Env\xEDo",
|
|
688
|
+
shippingFree: "Gratis",
|
|
689
|
+
taxes: "Impuestos",
|
|
690
|
+
total: "Total",
|
|
691
|
+
// Coupon
|
|
692
|
+
couponPlaceholder: "C\xF3digo promo",
|
|
693
|
+
couponApply: "Aplicar",
|
|
694
|
+
couponApplied: "\u2713 Cup\xF3n aplicado",
|
|
695
|
+
couponInvalid: "\u2717 C\xF3digo inv\xE1lido",
|
|
696
|
+
couponRemove: "Eliminar cup\xF3n",
|
|
697
|
+
// Sidebar
|
|
698
|
+
orderSummaryTitle: "Resumen del pedido",
|
|
699
|
+
securePayment: "PAGO SEGURO SSL",
|
|
700
|
+
// Actions
|
|
701
|
+
continueToPay: "Continuar al pago",
|
|
702
|
+
payNow: "Pagar ahora",
|
|
703
|
+
pay: "Pagar",
|
|
704
|
+
processing: "Procesando\u2026",
|
|
705
|
+
continueNow: "Continuar ahora",
|
|
706
|
+
// Payment Methods
|
|
707
|
+
paymentMethod: "M\xE9todo de pago",
|
|
708
|
+
back: "Volver",
|
|
709
|
+
completeDetails: "Completa los datos",
|
|
710
|
+
selectMethod: "Selecciona un m\xE9todo",
|
|
711
|
+
// Processing
|
|
712
|
+
processingPayment: "Procesando pago\u2026",
|
|
713
|
+
processingDescription: "No cierres ni recargues esta ventana.",
|
|
714
|
+
// Redirect
|
|
715
|
+
redirecting: "Redirigiendo\u2026",
|
|
716
|
+
redirectDescription: "Ser\xE1s redirigido para completar tu pago de forma segura.",
|
|
717
|
+
// Mobile bar
|
|
718
|
+
showOrderDetails: "Ver detalle del pedido",
|
|
719
|
+
hideOrderDetails: "Ocultar detalle del pedido",
|
|
720
|
+
continue: "Continuar"
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
// src/locales/pt-BR.ts
|
|
724
|
+
var ptBR = {
|
|
725
|
+
// Order Summary
|
|
726
|
+
orderSummary: "Resumo do pedido",
|
|
727
|
+
quantity: "Qtd",
|
|
728
|
+
quantityShort: "Qtd",
|
|
729
|
+
removeItem: "Remover item",
|
|
730
|
+
// Totals
|
|
731
|
+
subtotal: "Subtotal",
|
|
732
|
+
discount: "Desconto",
|
|
733
|
+
shipping: "Frete",
|
|
734
|
+
shippingFree: "Gr\xE1tis",
|
|
735
|
+
taxes: "Impostos",
|
|
736
|
+
total: "Total",
|
|
737
|
+
// Coupon
|
|
738
|
+
couponPlaceholder: "C\xF3digo promocional",
|
|
739
|
+
couponApply: "Aplicar",
|
|
740
|
+
couponApplied: "\u2713 Cupom aplicado",
|
|
741
|
+
couponInvalid: "\u2717 C\xF3digo inv\xE1lido",
|
|
742
|
+
couponRemove: "Remover cupom",
|
|
743
|
+
// Sidebar
|
|
744
|
+
orderSummaryTitle: "Resumo do pedido",
|
|
745
|
+
securePayment: "PAGAMENTO SEGURO SSL",
|
|
746
|
+
// Actions
|
|
747
|
+
continueToPay: "Continuar para pagamento",
|
|
748
|
+
payNow: "Pagar agora",
|
|
749
|
+
pay: "Pagar",
|
|
750
|
+
processing: "Processando\u2026",
|
|
751
|
+
continueNow: "Continuar agora",
|
|
752
|
+
// Payment Methods
|
|
753
|
+
paymentMethod: "M\xE9todo de pagamento",
|
|
754
|
+
back: "Voltar",
|
|
755
|
+
completeDetails: "Complete os dados",
|
|
756
|
+
selectMethod: "Selecione um m\xE9todo",
|
|
757
|
+
// Processing
|
|
758
|
+
processingPayment: "Processando pagamento\u2026",
|
|
759
|
+
processingDescription: "N\xE3o feche nem recarregue esta janela.",
|
|
760
|
+
// Redirect
|
|
761
|
+
redirecting: "Redirecionando\u2026",
|
|
762
|
+
redirectDescription: "Voc\xEA ser\xE1 redirecionado para completar seu pagamento com seguran\xE7a.",
|
|
763
|
+
// Mobile bar
|
|
764
|
+
showOrderDetails: "Ver detalhes do pedido",
|
|
765
|
+
hideOrderDetails: "Ocultar detalhes do pedido",
|
|
766
|
+
continue: "Continuar"
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/locales/index.ts
|
|
770
|
+
var localeMap = {
|
|
771
|
+
en,
|
|
772
|
+
es,
|
|
773
|
+
"pt-BR": ptBR
|
|
774
|
+
};
|
|
775
|
+
var SUPPORTED_LOCALES = Object.keys(localeMap);
|
|
776
|
+
function detectLocale() {
|
|
777
|
+
if (typeof navigator === "undefined") return "en";
|
|
778
|
+
const languages = navigator.languages ?? [navigator.language];
|
|
779
|
+
for (const lang of languages) {
|
|
780
|
+
if (SUPPORTED_LOCALES.includes(lang)) return lang;
|
|
781
|
+
const base = lang.split("-")[0];
|
|
782
|
+
if (SUPPORTED_LOCALES.includes(base)) return base;
|
|
783
|
+
if (base === "pt") return "pt-BR";
|
|
784
|
+
}
|
|
785
|
+
return "en";
|
|
786
|
+
}
|
|
787
|
+
function resolveLocale(locale) {
|
|
788
|
+
return locale === "auto" ? detectLocale() : locale;
|
|
789
|
+
}
|
|
790
|
+
function getMessages(locale) {
|
|
791
|
+
const resolved = resolveLocale(locale);
|
|
792
|
+
return localeMap[resolved] ?? en;
|
|
793
|
+
}
|
|
794
|
+
function resolveMessages(locale, overrides) {
|
|
795
|
+
const base = getMessages(locale);
|
|
796
|
+
if (!overrides) return base;
|
|
797
|
+
return { ...base, ...overrides };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// src/core/i18n-context.tsx
|
|
801
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
802
|
+
var I18nContext = (0, import_react5.createContext)(null);
|
|
803
|
+
var I18nProvider = ({ locale, currency, messages, formatters, children }) => {
|
|
804
|
+
const t = resolveMessages(locale, messages);
|
|
805
|
+
const fmt = formatters?.currency ? formatters.currency : (amount) => formatNumber(amount, currency);
|
|
806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(I18nContext.Provider, { value: { t, fmt }, children });
|
|
807
|
+
};
|
|
808
|
+
var useI18n = () => {
|
|
809
|
+
const ctx = (0, import_react5.useContext)(I18nContext);
|
|
810
|
+
if (!ctx) {
|
|
811
|
+
throw new Error("useI18n must be used within an I18nProvider");
|
|
812
|
+
}
|
|
813
|
+
return ctx;
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// src/plugins/plugin-context.tsx
|
|
817
|
+
var import_react6 = require("react");
|
|
818
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
819
|
+
var PluginContext = (0, import_react6.createContext)({ plugins: [] });
|
|
820
|
+
var PluginProvider = ({ plugins, children }) => {
|
|
821
|
+
const value = (0, import_react6.useMemo)(() => ({ plugins }), [plugins]);
|
|
822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PluginContext.Provider, { value, children });
|
|
823
|
+
};
|
|
824
|
+
var usePlugins = () => (0, import_react6.useContext)(PluginContext);
|
|
825
|
+
function renderSlot(plugins, slotName, props) {
|
|
826
|
+
return plugins.filter((p) => p.slots[slotName]).map((p) => {
|
|
827
|
+
const Slot = p.slots[slotName];
|
|
828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react6.Fragment, { children: Slot(props) }, p.id);
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
function renderItemSlot(plugins, slotName, props) {
|
|
832
|
+
return plugins.filter((p) => p.slots[slotName]).map((p) => {
|
|
833
|
+
const Slot = p.slots[slotName];
|
|
834
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react6.Fragment, { children: Slot(props) }, p.id);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/components/checkout-content.tsx
|
|
839
|
+
var import_lucide_react8 = require("lucide-react");
|
|
840
|
+
|
|
841
|
+
// src/components/OrderSummary.tsx
|
|
842
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
843
|
+
var OrderSummary = () => {
|
|
844
|
+
const { items, step } = useCheckout();
|
|
845
|
+
const { t, fmt } = useI18n();
|
|
846
|
+
const { plugins } = usePlugins();
|
|
847
|
+
const hasItemActions = plugins.some((p) => p.slots.orderItemActions);
|
|
848
|
+
const hasItemEnd = plugins.some((p) => p.slots.orderItemEnd);
|
|
849
|
+
const slotProps = { items, totals: { subtotal: 0, total: 0 }, step, t, fmt };
|
|
850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col h-full animate-in fade-in slide-in-from-bottom-4 duration-500 overflow-hidden", children: [
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "text-xl font-bold mb-6 shrink-0", children: t.orderSummary }),
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-1 overflow-y-auto pr-3 custom-scrollbar flex flex-col divide-y divide-border/30", children: items.map((item) => {
|
|
853
|
+
const discountedRow = !!item.discount;
|
|
854
|
+
const itemSlotProps = { ...slotProps, item };
|
|
855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
856
|
+
"div",
|
|
857
|
+
{
|
|
858
|
+
className: "py-5 flex items-start gap-5 transition-opacity",
|
|
859
|
+
children: [
|
|
860
|
+
item.image && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
861
|
+
"img",
|
|
862
|
+
{
|
|
863
|
+
src: item.image,
|
|
864
|
+
alt: item.name,
|
|
865
|
+
className: "w-20 h-20 rounded-2xl object-cover border border-border/40 shrink-0 shadow-sm transition-transform hover:scale-105 duration-300"
|
|
866
|
+
}
|
|
867
|
+
),
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-bold text-lg text-text/90 block truncate leading-tight mb-1", children: item.name }),
|
|
870
|
+
hasItemActions ? renderItemSlot(plugins, "orderItemActions", itemSlotProps) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-sm text-text/40 mt-1 block font-semibold uppercase tracking-wider", children: [
|
|
871
|
+
t.quantityShort,
|
|
872
|
+
": ",
|
|
873
|
+
item.quantity
|
|
874
|
+
] }),
|
|
875
|
+
discountedRow && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 mt-3 flex-wrap", children: [
|
|
876
|
+
item.discount.label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[10px] font-black uppercase tracking-widest bg-success/10 text-success px-2 py-1 rounded-md border border-success/20", children: item.discount.label }),
|
|
877
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs text-success font-black", children: item.discount.type === "percentage" ? `-${item.discount.value}%` : `-${fmt(item.discount.value)}` })
|
|
878
|
+
] })
|
|
879
|
+
] }),
|
|
880
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col items-end gap-2 shrink-0", children: [
|
|
881
|
+
hasItemEnd && renderItemSlot(plugins, "orderItemEnd", itemSlotProps),
|
|
882
|
+
discountedRow ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("flex flex-col items-end", hasItemEnd ? "" : "mt-1"), children: [
|
|
883
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs line-through text-text/30 font-medium tracking-tight", children: fmt(item.unitPrice * item.quantity) }),
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-lg font-black text-success tracking-tight", children: fmt(item.total) })
|
|
885
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: cn("text-lg font-bold text-text/80 tracking-tight", hasItemEnd ? "" : "mt-1"), children: fmt(item.total) })
|
|
886
|
+
] })
|
|
887
|
+
]
|
|
888
|
+
},
|
|
889
|
+
item.id
|
|
890
|
+
);
|
|
891
|
+
}) })
|
|
892
|
+
] });
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
// src/components/PaymentMethods.tsx
|
|
896
|
+
var import_react7 = require("react");
|
|
897
|
+
var import_lucide_react3 = require("lucide-react");
|
|
898
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
899
|
+
var getDefaultIcon = (type) => {
|
|
900
|
+
switch (type) {
|
|
901
|
+
case "card":
|
|
902
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.CreditCard, { className: "w-5 h-5" });
|
|
903
|
+
case "wallet":
|
|
904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Wallet, { className: "w-5 h-5" });
|
|
905
|
+
case "bank":
|
|
906
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Landmark, { className: "w-5 h-5" });
|
|
907
|
+
default:
|
|
908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Wallet, { className: "w-5 h-5" });
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
var PaymentMethods = () => {
|
|
912
|
+
const { paymentMethods, selectedMethodId, selectMethod, setStep, submitPayment, totals, error } = useCheckout();
|
|
913
|
+
const { t, fmt } = useI18n();
|
|
914
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react7.useState)(false);
|
|
915
|
+
const [readyMap, setReadyMap] = (0, import_react7.useState)({});
|
|
916
|
+
const selectedMethod = paymentMethods.find((m) => m.id === selectedMethodId);
|
|
917
|
+
const isReady = selectedMethod ? selectedMethod.renderComponent ? !!readyMap[selectedMethod.id] : true : false;
|
|
918
|
+
const handleSelectMethod = (id) => {
|
|
919
|
+
selectMethod(id);
|
|
920
|
+
};
|
|
921
|
+
const makeReadyCallback = (0, import_react7.useCallback)(
|
|
922
|
+
(methodId) => (ready) => {
|
|
923
|
+
setReadyMap((prev) => ({ ...prev, [methodId]: ready }));
|
|
924
|
+
},
|
|
925
|
+
[]
|
|
926
|
+
);
|
|
927
|
+
const handleSubmit = async (e) => {
|
|
928
|
+
e.preventDefault();
|
|
929
|
+
if (!selectedMethodId || !isReady) return;
|
|
930
|
+
setIsSubmitting(true);
|
|
931
|
+
await submitPayment({ methodId: selectedMethodId });
|
|
932
|
+
setIsSubmitting(false);
|
|
933
|
+
};
|
|
934
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
935
|
+
"form",
|
|
936
|
+
{
|
|
937
|
+
id: "checkout-payment-form",
|
|
938
|
+
onSubmit: handleSubmit,
|
|
939
|
+
className: "flex flex-col gap-3 animate-in fade-in slide-in-from-right-4 duration-500",
|
|
940
|
+
children: [
|
|
941
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
943
|
+
"button",
|
|
944
|
+
{
|
|
945
|
+
type: "button",
|
|
946
|
+
onClick: () => setStep("review"),
|
|
947
|
+
className: "p-1 hover:bg-border/50 rounded-full transition-colors text-text/80",
|
|
948
|
+
"aria-label": t.back,
|
|
949
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.ChevronLeft, { className: "w-5 h-5" })
|
|
950
|
+
}
|
|
951
|
+
),
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "text-xl font-semibold", children: t.paymentMethod })
|
|
953
|
+
] }),
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 overflow-y-auto pr-1 custom-scrollbar flex flex-col gap-2", children: paymentMethods.map((method) => {
|
|
955
|
+
const isSelected = selectedMethodId === method.id;
|
|
956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col", children: [
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
958
|
+
"button",
|
|
959
|
+
{
|
|
960
|
+
type: "button",
|
|
961
|
+
onClick: () => handleSelectMethod(method.id),
|
|
962
|
+
disabled: isSubmitting,
|
|
963
|
+
className: cn(
|
|
964
|
+
"flex items-center gap-3 p-3.5 border transition-all duration-200 text-left",
|
|
965
|
+
isSelected ? "border-primary bg-primary/5 ring-1 ring-primary shadow-sm" : "border-border/60 hover:border-primary/50 bg-background/50 hover:bg-background",
|
|
966
|
+
isSelected && method.renderComponent ? "rounded-t-[var(--radius-DEFAULT)]" : "rounded-[var(--radius-DEFAULT)]"
|
|
967
|
+
),
|
|
968
|
+
children: [
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: cn("p-1.5 rounded-lg shrink-0", isSelected ? "text-primary" : "text-text/50"), children: getDefaultIcon(method.type) }),
|
|
970
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "flex-1 font-bold text-sm tracking-tight", children: method.label }),
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
972
|
+
"div",
|
|
973
|
+
{
|
|
974
|
+
className: cn(
|
|
975
|
+
"w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all shrink-0",
|
|
976
|
+
isSelected ? "border-primary bg-primary/10" : "border-text/20"
|
|
977
|
+
),
|
|
978
|
+
children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-2.5 h-2.5 rounded-full bg-primary shadow-sm" })
|
|
979
|
+
}
|
|
980
|
+
)
|
|
981
|
+
]
|
|
982
|
+
}
|
|
983
|
+
),
|
|
984
|
+
isSelected && method.renderComponent && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "border border-t-0 border-primary/20 bg-background/50 rounded-b-[var(--radius-DEFAULT)] px-5 pt-5 pb-5 animate-in fade-in slide-in-from-top-2 duration-300", children: method.renderComponent({ onReadyChange: makeReadyCallback(method.id) }) })
|
|
985
|
+
] }, method.id);
|
|
986
|
+
}) }),
|
|
987
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
988
|
+
"button",
|
|
989
|
+
{
|
|
990
|
+
type: "submit",
|
|
991
|
+
disabled: !isReady || isSubmitting,
|
|
992
|
+
className: cn(
|
|
993
|
+
"md:hidden flex w-full py-4 px-4 rounded-[var(--radius-DEFAULT)] mt-2 font-black uppercase tracking-widest justify-center items-center gap-2 transition-all shadow-lg active:scale-[0.98]",
|
|
994
|
+
!isReady || isSubmitting ? "bg-primary/40 text-surface cursor-not-allowed" : "bg-primary text-surface hover:brightness-110 shadow-primary/20"
|
|
995
|
+
),
|
|
996
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
997
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react3.Loader2, { className: "w-5 h-5 animate-spin" }),
|
|
998
|
+
t.processing
|
|
999
|
+
] }) : isReady ? `${t.pay} ${fmt(totals.total)}` : selectedMethod ? t.completeDetails : t.selectMethod
|
|
1000
|
+
}
|
|
1001
|
+
),
|
|
1002
|
+
error && !isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-sm text-error text-center font-medium animate-in fade-in duration-300 -mt-1", children: error })
|
|
1003
|
+
]
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// src/components/FeedbackScreen.tsx
|
|
1009
|
+
var import_react8 = require("react");
|
|
1010
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1011
|
+
|
|
1012
|
+
// src/components/button.tsx
|
|
1013
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1014
|
+
var Button = ({
|
|
1015
|
+
label,
|
|
1016
|
+
onPress,
|
|
1017
|
+
variant = "solid",
|
|
1018
|
+
icon,
|
|
1019
|
+
disabled,
|
|
1020
|
+
className,
|
|
1021
|
+
type = "button"
|
|
1022
|
+
}) => {
|
|
1023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1024
|
+
"button",
|
|
1025
|
+
{
|
|
1026
|
+
type,
|
|
1027
|
+
onClick: onPress,
|
|
1028
|
+
disabled,
|
|
1029
|
+
className: cn(
|
|
1030
|
+
"flex items-center justify-center gap-2 px-5 py-2.5 rounded-[var(--radius-DEFAULT)] font-medium text-sm transition-all active:scale-[0.98]",
|
|
1031
|
+
variant === "solid" && "bg-primary text-surface hover:opacity-90 disabled:opacity-40 disabled:cursor-not-allowed",
|
|
1032
|
+
variant === "outline" && "border border-primary text-primary hover:bg-primary/5 disabled:opacity-40 disabled:cursor-not-allowed",
|
|
1033
|
+
variant === "ghost" && "text-text/70 hover:bg-border/50 hover:text-text disabled:opacity-40 disabled:cursor-not-allowed",
|
|
1034
|
+
className
|
|
1035
|
+
),
|
|
1036
|
+
children: [
|
|
1037
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "shrink-0", children: icon }),
|
|
1038
|
+
label
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
);
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
// src/components/FeedbackScreen.tsx
|
|
1045
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1046
|
+
var REDIRECT_DELAY = 2;
|
|
1047
|
+
var variantConfig = {
|
|
1048
|
+
success: {
|
|
1049
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.CheckCircle2, { className: "w-10 h-10" }),
|
|
1050
|
+
iconBg: "bg-success/10",
|
|
1051
|
+
iconColor: "text-success"
|
|
1052
|
+
},
|
|
1053
|
+
pending: {
|
|
1054
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Clock, { className: "w-10 h-10" }),
|
|
1055
|
+
iconBg: "bg-orange-500/10",
|
|
1056
|
+
iconColor: "text-orange-500"
|
|
1057
|
+
},
|
|
1058
|
+
error: {
|
|
1059
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.XCircle, { className: "w-10 h-10" }),
|
|
1060
|
+
iconBg: "bg-error/10",
|
|
1061
|
+
iconColor: "text-error"
|
|
1062
|
+
},
|
|
1063
|
+
redirect: {
|
|
1064
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.ExternalLink, { className: "w-10 h-10" }),
|
|
1065
|
+
iconBg: "bg-primary/10",
|
|
1066
|
+
iconColor: "text-primary"
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
var FeedbackScreen = ({
|
|
1070
|
+
title,
|
|
1071
|
+
description,
|
|
1072
|
+
actions = [],
|
|
1073
|
+
variant = "success",
|
|
1074
|
+
children
|
|
1075
|
+
}) => {
|
|
1076
|
+
const config = variantConfig[variant];
|
|
1077
|
+
const [countdown, setCountdown] = (0, import_react8.useState)(variant === "redirect" ? REDIRECT_DELAY : 0);
|
|
1078
|
+
(0, import_react8.useEffect)(() => {
|
|
1079
|
+
if (variant !== "redirect") return;
|
|
1080
|
+
if (countdown <= 0) {
|
|
1081
|
+
const primaryAction = actions.find((a) => !a.variant || a.variant === "solid");
|
|
1082
|
+
primaryAction?.onPress();
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const timer = setInterval(() => setCountdown((c) => c - 1), 1e3);
|
|
1086
|
+
return () => clearInterval(timer);
|
|
1087
|
+
}, [countdown, variant]);
|
|
1088
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center justify-center py-8 gap-5 animate-in zoom-in-95 fade-in duration-500", children: [
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1090
|
+
"div",
|
|
1091
|
+
{
|
|
1092
|
+
className: cn(
|
|
1093
|
+
"w-20 h-20 rounded-full flex items-center justify-center",
|
|
1094
|
+
config.iconBg,
|
|
1095
|
+
config.iconColor
|
|
1096
|
+
),
|
|
1097
|
+
children: config.icon
|
|
1098
|
+
}
|
|
1099
|
+
),
|
|
1100
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-center", children: [
|
|
1101
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-2xl font-bold mb-1", children: title }),
|
|
1102
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-text/60 text-sm max-w-[280px] mx-auto leading-relaxed", children: description })
|
|
1103
|
+
] }),
|
|
1104
|
+
variant === "redirect" && countdown > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center gap-2", children: [
|
|
1105
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative w-14 h-14", children: [
|
|
1106
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { className: "w-14 h-14 -rotate-90", viewBox: "0 0 56 56", children: [
|
|
1107
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1108
|
+
"circle",
|
|
1109
|
+
{
|
|
1110
|
+
cx: "28",
|
|
1111
|
+
cy: "28",
|
|
1112
|
+
r: "24",
|
|
1113
|
+
fill: "none",
|
|
1114
|
+
stroke: "currentColor",
|
|
1115
|
+
strokeWidth: "4",
|
|
1116
|
+
className: "text-border"
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1120
|
+
"circle",
|
|
1121
|
+
{
|
|
1122
|
+
cx: "28",
|
|
1123
|
+
cy: "28",
|
|
1124
|
+
r: "24",
|
|
1125
|
+
fill: "none",
|
|
1126
|
+
stroke: "currentColor",
|
|
1127
|
+
strokeWidth: "4",
|
|
1128
|
+
strokeLinecap: "round",
|
|
1129
|
+
strokeDasharray: `${2 * Math.PI * 24}`,
|
|
1130
|
+
strokeDashoffset: `${2 * Math.PI * 24 * (1 - countdown / REDIRECT_DELAY)}`,
|
|
1131
|
+
className: "text-primary transition-all duration-1000"
|
|
1132
|
+
}
|
|
1133
|
+
)
|
|
1134
|
+
] }),
|
|
1135
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "absolute inset-0 flex items-center justify-center text-xl font-bold text-primary", children: countdown })
|
|
1136
|
+
] }),
|
|
1137
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "text-xs text-text/50", children: [
|
|
1138
|
+
"Redirigiendo en ",
|
|
1139
|
+
countdown,
|
|
1140
|
+
"s\u2026"
|
|
1141
|
+
] })
|
|
1142
|
+
] }),
|
|
1143
|
+
children && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-full", children }),
|
|
1144
|
+
actions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-3 justify-center w-full mt-1", children: actions.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1145
|
+
Button,
|
|
1146
|
+
{
|
|
1147
|
+
label: action.label,
|
|
1148
|
+
onPress: action.onPress,
|
|
1149
|
+
variant: action.variant ?? "solid",
|
|
1150
|
+
icon: action.icon,
|
|
1151
|
+
className: action.variant === "solid" ? "flex-1" : void 0
|
|
1152
|
+
},
|
|
1153
|
+
i
|
|
1154
|
+
)) })
|
|
1155
|
+
] });
|
|
1156
|
+
};
|
|
1157
|
+
|
|
1158
|
+
// src/components/CheckoutLayout.tsx
|
|
1159
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1160
|
+
var CheckoutLayout = ({ children, sidebar, className, ...props }) => {
|
|
1161
|
+
const hasSidebar = !!sidebar;
|
|
1162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1163
|
+
"div",
|
|
1164
|
+
{
|
|
1165
|
+
className: cn(
|
|
1166
|
+
"w-full h-full bg-surface text-text rounded-(--theme-radius) shadow-2xl border border-border",
|
|
1167
|
+
"overflow-hidden flex flex-col md:flex-row",
|
|
1168
|
+
className
|
|
1169
|
+
),
|
|
1170
|
+
...props,
|
|
1171
|
+
children: [
|
|
1172
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1173
|
+
"div",
|
|
1174
|
+
{
|
|
1175
|
+
className: cn(
|
|
1176
|
+
"flex-1 min-w-0 min-h-0 flex flex-col overflow-hidden p-4"
|
|
1177
|
+
),
|
|
1178
|
+
children
|
|
1179
|
+
}
|
|
1180
|
+
),
|
|
1181
|
+
hasSidebar && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "hidden md:block w-px bg-border/60 shrink-0" }),
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "hidden md:flex w-[320px] shrink-0 flex-col overflow-y-auto p-4 bg-background/60", children: sidebar })
|
|
1184
|
+
] })
|
|
1185
|
+
]
|
|
1186
|
+
}
|
|
1187
|
+
);
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
// src/components/dev-tools.tsx
|
|
1191
|
+
var import_react9 = require("react");
|
|
1192
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1193
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1194
|
+
var SCENARIOS = ["success", "pending", "error", "redirect"];
|
|
1195
|
+
var SCENARIO_LABELS = {
|
|
1196
|
+
success: "\u2705 \xC9xito",
|
|
1197
|
+
pending: "\u23F3 Pendiente",
|
|
1198
|
+
error: "\u274C Error",
|
|
1199
|
+
redirect: "\u2197\uFE0F Redirect"
|
|
1200
|
+
};
|
|
1201
|
+
var DevTools = () => {
|
|
1202
|
+
const { state, eventLog } = useCheckoutContext();
|
|
1203
|
+
const [showDevTools, setShowDevTools] = (0, import_react9.useState)(false);
|
|
1204
|
+
const [scenario, setScenario] = (0, import_react9.useState)("success");
|
|
1205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1207
|
+
"button",
|
|
1208
|
+
{
|
|
1209
|
+
onClick: () => setShowDevTools(true),
|
|
1210
|
+
className: "fixed top-4 right-4 z-40 bg-surface text-text/50 hover:text-primary p-3 rounded-full shadow-md border border-border transition-all hover:scale-105",
|
|
1211
|
+
"aria-label": "Open Dev Tools",
|
|
1212
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Code2, { className: "w-5 h-5" })
|
|
1213
|
+
}
|
|
1214
|
+
),
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1216
|
+
"div",
|
|
1217
|
+
{
|
|
1218
|
+
className: `fixed top-0 right-0 h-full w-80 bg-surface shadow-2xl z-50 p-6 flex flex-col gap-6 overflow-y-auto transition-transform duration-300 ${showDevTools ? "translate-x-0" : "translate-x-full"}`,
|
|
1219
|
+
style: { colorScheme: "light" },
|
|
1220
|
+
children: [
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center justify-between pb-4 border-b border-border", children: [
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("h2", { className: "text-lg font-bold text-text flex items-center gap-2", children: [
|
|
1223
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.Code2, { className: "w-5 h-5 text-primary" }),
|
|
1224
|
+
"Dev Tools"
|
|
1225
|
+
] }),
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1227
|
+
"button",
|
|
1228
|
+
{
|
|
1229
|
+
onClick: () => setShowDevTools(false),
|
|
1230
|
+
className: "text-text/50 hover:text-text p-1 bg-surface hover:bg-border/50 rounded-full transition-colors",
|
|
1231
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.X, { className: "w-5 h-5" })
|
|
1232
|
+
}
|
|
1233
|
+
)
|
|
1234
|
+
] }),
|
|
1235
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
1236
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-sm font-bold mb-3 text-slate-500 uppercase tracking-wider", children: "Escenario de pago" }),
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col gap-1", children: SCENARIOS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer p-2 hover:bg-slate-50 rounded-lg", children: [
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1239
|
+
"input",
|
|
1240
|
+
{
|
|
1241
|
+
type: "radio",
|
|
1242
|
+
name: "scenario",
|
|
1243
|
+
checked: scenario === s,
|
|
1244
|
+
onChange: () => setScenario(s),
|
|
1245
|
+
className: "accent-indigo-500 w-4 h-4"
|
|
1246
|
+
}
|
|
1247
|
+
),
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-sm", children: SCENARIO_LABELS[s] })
|
|
1249
|
+
] }, s)) }),
|
|
1250
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[11px] text-slate-400 mt-2 p-2 bg-slate-50 rounded leading-relaxed", children: [
|
|
1251
|
+
"Cup\xF3n de prueba: ",
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { className: "font-mono font-bold text-slate-600", children: "SAVE10" })
|
|
1253
|
+
] })
|
|
1254
|
+
] }),
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("h2", { className: "text-sm font-bold mb-3 text-slate-500 uppercase tracking-wider flex items-center gap-2", children: [
|
|
1257
|
+
"Event Logger ",
|
|
1258
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-[10px] bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded-full font-semibold", children: "Live" })
|
|
1259
|
+
] }),
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "bg-slate-900 text-emerald-400 font-mono text-[10px] p-3 rounded-xl h-56 overflow-y-auto flex flex-col gap-1", children: eventLog.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "text-slate-500 italic", children: "Sin eventos a\xFAn\u2026" }) : eventLog.map((log, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "border-b border-white/10 pb-1", children: log }, i)) })
|
|
1261
|
+
] }),
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { className: "text-sm font-bold text-slate-500 uppercase tracking-wider", children: "Estado interno" }),
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("pre", { className: "text-[10px] bg-slate-900 text-emerald-400 p-3 rounded-xl overflow-x-auto", children: JSON.stringify({ ...state, provider: "PaymentProvider instance" }, null, 2) })
|
|
1265
|
+
] })
|
|
1266
|
+
]
|
|
1267
|
+
}
|
|
1268
|
+
)
|
|
1269
|
+
] });
|
|
1270
|
+
};
|
|
1271
|
+
var dev_tools_default = DevTools;
|
|
1272
|
+
|
|
1273
|
+
// src/components/OrderSidebarSummary.tsx
|
|
1274
|
+
var import_lucide_react6 = require("lucide-react");
|
|
1275
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1276
|
+
var OrderSidebarSummary = ({ showItems: propShowItems, isLoading }) => {
|
|
1277
|
+
const { items, totals, step, setStep, selectedMethodId } = useCheckout();
|
|
1278
|
+
const { t, fmt } = useI18n();
|
|
1279
|
+
const { plugins } = usePlugins();
|
|
1280
|
+
const showItems = propShowItems ?? step !== "review";
|
|
1281
|
+
const handleAction = () => {
|
|
1282
|
+
if (step === "review") {
|
|
1283
|
+
setStep("payment_method");
|
|
1284
|
+
} else {
|
|
1285
|
+
const form = document.getElementById("checkout-payment-form");
|
|
1286
|
+
form?.requestSubmit();
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
const actionLabel = step === "review" ? t.continueToPay : t.payNow;
|
|
1290
|
+
const isReady = step === "review" || !!selectedMethodId;
|
|
1291
|
+
const slotProps = { items, totals, step, t, fmt };
|
|
1292
|
+
const hasDiscountAction = plugins.some((p) => p.slots.totalsDiscountAction);
|
|
1293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col h-full animate-in fade-in duration-500", children: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { className: "text-xs font-bold text-text/40 uppercase tracking-widest mb-6", children: t.orderSummaryTitle }),
|
|
1295
|
+
showItems && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-col gap-4 overflow-y-auto pr-2 custom-scrollbar", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-start gap-3", children: [
|
|
1296
|
+
item.image && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1297
|
+
"img",
|
|
1298
|
+
{
|
|
1299
|
+
src: item.image,
|
|
1300
|
+
alt: item.name,
|
|
1301
|
+
className: "w-10 h-10 rounded-lg object-cover border border-border/40 shrink-0 shadow-sm"
|
|
1302
|
+
}
|
|
1303
|
+
),
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1305
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[13px] font-semibold leading-tight truncate text-text/80", children: item.name }),
|
|
1306
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "text-[11px] text-text/40 mt-0.5 font-medium", children: [
|
|
1307
|
+
t.quantityShort,
|
|
1308
|
+
": ",
|
|
1309
|
+
item.quantity
|
|
1310
|
+
] })
|
|
1311
|
+
] }),
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "text-right shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[13px] font-bold text-text/80", children: fmt(item.total) }) })
|
|
1313
|
+
] }, item.id)) }),
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1315
|
+
renderSlot(plugins, "sidebarBeforeTotals", slotProps),
|
|
1316
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-t border-border/50 justify-between text-sm text-text/60 font-medium", children: [
|
|
1317
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.subtotal }),
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: fmt(totals.subtotal) })
|
|
1319
|
+
] }),
|
|
1320
|
+
totals.discount !== void 0 && totals.discount > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between items-center text-sm font-semibold text-success", children: [
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.discount }),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1.5", children: [
|
|
1323
|
+
"-",
|
|
1324
|
+
fmt(totals.discount),
|
|
1325
|
+
hasDiscountAction && renderSlot(plugins, "totalsDiscountAction", slotProps)
|
|
1326
|
+
] })
|
|
1327
|
+
] }),
|
|
1328
|
+
totals.shipping !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between text-sm text-text/60 font-medium", children: [
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.shipping }),
|
|
1330
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: totals.shipping === 0 ? t.shippingFree : fmt(totals.shipping) })
|
|
1331
|
+
] }),
|
|
1332
|
+
totals.taxes !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between text-sm text-text/60 font-medium", children: [
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.taxes }),
|
|
1334
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: fmt(totals.taxes) })
|
|
1335
|
+
] }),
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-between items-center pt-4 border-t border-border/50 mt-2", children: [
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-bold text-base text-text/90", children: t.total }),
|
|
1338
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-2xl font-black text-primary tracking-tight", children: fmt(totals.total) })
|
|
1339
|
+
] })
|
|
1340
|
+
] }),
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-2 mt-auto", children: [
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1343
|
+
"button",
|
|
1344
|
+
{
|
|
1345
|
+
onClick: handleAction,
|
|
1346
|
+
disabled: !isReady || isLoading,
|
|
1347
|
+
className: cn(
|
|
1348
|
+
"w-full mt-6 py-4 px-6 rounded-xs font-bold flex items-center justify-center gap-2 transition-all duration-300 shadow-lg active:scale-[0.98]",
|
|
1349
|
+
isReady && !isLoading ? "bg-primary text-surface hover:brightness-110 shadow-primary/20 cursor-pointer" : "bg-primary/40 text-surface/60 cursor-not-allowed shadow-none"
|
|
1350
|
+
),
|
|
1351
|
+
children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react6.Loader2, { className: "w-5 h-5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1352
|
+
actionLabel,
|
|
1353
|
+
step === "review" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react6.ChevronRight, { className: "w-5 h-5" })
|
|
1354
|
+
] })
|
|
1355
|
+
}
|
|
1356
|
+
),
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-center gap-2 mt-4 text-[11px] text-text/30 font-semibold tracking-wide", children: [
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react6.ShieldCheck, { className: "w-4 h-4 opacity-50" }),
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: t.securePayment })
|
|
1360
|
+
] })
|
|
1361
|
+
] })
|
|
1362
|
+
] });
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
// src/components/MobileOrderBar.tsx
|
|
1366
|
+
var import_react10 = require("react");
|
|
1367
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1368
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1369
|
+
var MobileOrderBar = ({
|
|
1370
|
+
onAction,
|
|
1371
|
+
actionLabel,
|
|
1372
|
+
actionDisabled,
|
|
1373
|
+
isSubmitting,
|
|
1374
|
+
showItems = false
|
|
1375
|
+
}) => {
|
|
1376
|
+
const { totals, items, step } = useCheckout();
|
|
1377
|
+
const { t, fmt } = useI18n();
|
|
1378
|
+
const { plugins } = usePlugins();
|
|
1379
|
+
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
1380
|
+
const slotProps = { items, totals, step, t, fmt };
|
|
1381
|
+
const hasDiscountAction = plugins.some((p) => p.slots.totalsDiscountAction);
|
|
1382
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "fixed bottom-0 left-0 right-0 z-30 md:hidden", children: [
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1384
|
+
"div",
|
|
1385
|
+
{
|
|
1386
|
+
className: `bg-surface border-t border-border overflow-hidden transition-all duration-300 ease-in-out ${expanded ? "max-h-[70vh] opacity-100" : "max-h-0 opacity-0 pointer-events-none"}`,
|
|
1387
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "px-4 pt-4 pb-2 flex flex-col gap-0", children: [
|
|
1388
|
+
showItems && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "overflow-y-auto max-h-52 divide-y divide-border/40 mb-3", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-3 py-2.5", children: [
|
|
1389
|
+
item.image && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1390
|
+
"img",
|
|
1391
|
+
{
|
|
1392
|
+
src: item.image,
|
|
1393
|
+
alt: item.name,
|
|
1394
|
+
className: "w-10 h-10 rounded-lg object-cover border border-border/30 shrink-0"
|
|
1395
|
+
}
|
|
1396
|
+
),
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium truncate", children: item.name }),
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-xs text-text/50", children: [
|
|
1400
|
+
t.quantityShort,
|
|
1401
|
+
": ",
|
|
1402
|
+
item.quantity
|
|
1403
|
+
] }),
|
|
1404
|
+
item.discount && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("p", { className: "text-[10px] text-success font-medium", children: [
|
|
1405
|
+
item.discount.label && `${item.discount.label} \xB7 `,
|
|
1406
|
+
item.discount.type === "percentage" ? `-${item.discount.value}%` : `-${fmt(item.discount.value)}`
|
|
1407
|
+
] })
|
|
1408
|
+
] }),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "text-right shrink-0", children: item.discount ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col items-end", children: [
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-[11px] line-through text-text/40", children: fmt(item.unitPrice * item.quantity) }),
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-semibold text-success", children: fmt(item.total) })
|
|
1412
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium", children: fmt(item.total) }) })
|
|
1413
|
+
] }, item.id)) }),
|
|
1414
|
+
renderSlot(plugins, "mobileBarExpanded", slotProps),
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: `flex flex-col gap-1.5 text-sm pb-3 ${showItems ? "border-t border-border/50 pt-3" : "pt-1"}`, children: [
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between text-text/60", children: [
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t.subtotal }),
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: fmt(totals.subtotal) })
|
|
1419
|
+
] }),
|
|
1420
|
+
totals.discount !== void 0 && totals.discount > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between items-center text-success font-medium", children: [
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t.discount }),
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "flex items-center gap-1.5", children: [
|
|
1423
|
+
"-",
|
|
1424
|
+
fmt(totals.discount),
|
|
1425
|
+
hasDiscountAction && renderSlot(plugins, "totalsDiscountAction", slotProps)
|
|
1426
|
+
] })
|
|
1427
|
+
] }),
|
|
1428
|
+
totals.shipping !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between text-text/60", children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t.shipping }),
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: totals.shipping === 0 ? t.shippingFree : fmt(totals.shipping) })
|
|
1431
|
+
] }),
|
|
1432
|
+
totals.taxes !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex justify-between text-text/60", children: [
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: t.taxes }),
|
|
1434
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: fmt(totals.taxes) })
|
|
1435
|
+
] })
|
|
1436
|
+
] })
|
|
1437
|
+
] })
|
|
1438
|
+
}
|
|
1439
|
+
),
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "bg-surface border-t border-border shadow-[0_-4px_20px_rgba(0,0,0,0.1)] px-4 pt-3 pb-4 flex flex-col gap-3", children: [
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
1442
|
+
"button",
|
|
1443
|
+
{
|
|
1444
|
+
type: "button",
|
|
1445
|
+
onClick: () => setExpanded((v) => !v),
|
|
1446
|
+
className: "flex items-center justify-between w-full",
|
|
1447
|
+
"aria-label": expanded ? t.hideOrderDetails : t.showOrderDetails,
|
|
1448
|
+
children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-sm font-medium text-text/60", children: t.total }),
|
|
1450
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "flex items-center gap-1.5", children: [
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-lg font-bold text-primary", children: fmt(totals.total) }),
|
|
1452
|
+
expanded ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react7.ChevronDown, { className: "w-4 h-4 text-text/40" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react7.ChevronUp, { className: "w-4 h-4 text-text/40" })
|
|
1453
|
+
] })
|
|
1454
|
+
]
|
|
1455
|
+
}
|
|
1456
|
+
),
|
|
1457
|
+
onAction && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1458
|
+
"button",
|
|
1459
|
+
{
|
|
1460
|
+
type: "button",
|
|
1461
|
+
onClick: onAction,
|
|
1462
|
+
disabled: actionDisabled || isSubmitting,
|
|
1463
|
+
className: `w-full py-3 rounded-[var(--radius-DEFAULT)] font-medium text-sm flex items-center justify-center gap-2 transition-all active:scale-[0.98] ${actionDisabled || isSubmitting ? "bg-primary/40 text-surface cursor-not-allowed" : "bg-primary text-surface hover:opacity-90"}`,
|
|
1464
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react7.Loader2, { className: "w-4 h-4 animate-spin" }),
|
|
1466
|
+
t.processing
|
|
1467
|
+
] }) : actionLabel ?? t.continue
|
|
1468
|
+
}
|
|
1469
|
+
)
|
|
1470
|
+
] })
|
|
1471
|
+
] });
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
// src/components/mobile-bar.tsx
|
|
1475
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1476
|
+
var MobileBar = ({ step }) => {
|
|
1477
|
+
const { setStep } = useCheckout();
|
|
1478
|
+
const { t } = useI18n();
|
|
1479
|
+
if (isReviewStep(step)) {
|
|
1480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1481
|
+
MobileOrderBar,
|
|
1482
|
+
{
|
|
1483
|
+
onAction: () => setStep("payment_method"),
|
|
1484
|
+
actionLabel: `${t.continueToPay} \u2192`,
|
|
1485
|
+
showItems: false
|
|
1486
|
+
}
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
if (isPaymentStep(step)) {
|
|
1490
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
1491
|
+
MobileOrderBar,
|
|
1492
|
+
{
|
|
1493
|
+
onAction: () => {
|
|
1494
|
+
const form = document.getElementById("checkout-payment-form");
|
|
1495
|
+
form?.requestSubmit();
|
|
1496
|
+
},
|
|
1497
|
+
actionLabel: t.pay,
|
|
1498
|
+
showItems: true
|
|
1499
|
+
}
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
return null;
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
// src/components/checkout-content.tsx
|
|
1506
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1507
|
+
var CheckoutContent = ({ devTools }) => {
|
|
1508
|
+
const { state } = useCheckoutContext();
|
|
1509
|
+
const { t } = useI18n();
|
|
1510
|
+
const { step, paymentResult } = state;
|
|
1511
|
+
const renderStep = () => {
|
|
1512
|
+
switch (step) {
|
|
1513
|
+
case "review":
|
|
1514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OrderSummary, {});
|
|
1515
|
+
case "payment_method":
|
|
1516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PaymentMethods, {});
|
|
1517
|
+
case "processing":
|
|
1518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 gap-4", children: [
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react8.Loader2, { className: "w-12 h-12 animate-spin text-primary" }),
|
|
1520
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "font-medium text-lg animate-pulse", children: t.processingPayment }),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm text-text/50", children: t.processingDescription })
|
|
1522
|
+
] });
|
|
1523
|
+
case "success":
|
|
1524
|
+
case "pending":
|
|
1525
|
+
case "error":
|
|
1526
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1527
|
+
FeedbackScreen,
|
|
1528
|
+
{
|
|
1529
|
+
variant: paymentResult?.status,
|
|
1530
|
+
title: paymentResult?.feedback?.title ?? "",
|
|
1531
|
+
description: paymentResult?.feedback?.title,
|
|
1532
|
+
actions: paymentResult?.feedback?.actions
|
|
1533
|
+
}
|
|
1534
|
+
);
|
|
1535
|
+
case "redirect": {
|
|
1536
|
+
const result = paymentResult?.status === "redirect" ? paymentResult : void 0;
|
|
1537
|
+
const fb = result?.feedback;
|
|
1538
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1539
|
+
FeedbackScreen,
|
|
1540
|
+
{
|
|
1541
|
+
variant: "redirect",
|
|
1542
|
+
title: fb?.title ?? t.redirecting,
|
|
1543
|
+
description: fb?.description ?? t.redirectDescription,
|
|
1544
|
+
actions: fb?.actions ?? [
|
|
1545
|
+
{
|
|
1546
|
+
label: t.continueNow,
|
|
1547
|
+
variant: "solid",
|
|
1548
|
+
onPress: () => {
|
|
1549
|
+
if (result?.url) window.location.href = result.url;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
]
|
|
1553
|
+
}
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
default:
|
|
1557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OrderSummary, {});
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "h-[80vh] md:h-full w-full flex flex-col", children: [
|
|
1561
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CheckoutLayout, { sidebar: isReviewStep(step) || isPaymentStep(step) ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OrderSidebarSummary, {}) : void 0, children: renderStep() }),
|
|
1562
|
+
(isReviewStep(step) || isPaymentStep(step)) && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MobileBar, { step }),
|
|
1563
|
+
devTools && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(dev_tools_default, {})
|
|
1564
|
+
] });
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1567
|
+
// src/components/Checkout.tsx
|
|
1568
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1569
|
+
var Checkout = ({
|
|
1570
|
+
items,
|
|
1571
|
+
totals,
|
|
1572
|
+
paymentMethods,
|
|
1573
|
+
provider,
|
|
1574
|
+
currency,
|
|
1575
|
+
locale = "es",
|
|
1576
|
+
theme = "light",
|
|
1577
|
+
brandColor,
|
|
1578
|
+
customTheme,
|
|
1579
|
+
onEvent,
|
|
1580
|
+
cartController,
|
|
1581
|
+
initialState,
|
|
1582
|
+
devTools,
|
|
1583
|
+
messages,
|
|
1584
|
+
formatters,
|
|
1585
|
+
plugins = []
|
|
1586
|
+
}) => {
|
|
1587
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ThemeProvider, { theme, brandColor, customTheme, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(I18nProvider, { locale, currency, messages, formatters, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(PluginProvider, { plugins, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1588
|
+
CheckoutProvider,
|
|
1589
|
+
{
|
|
1590
|
+
config: { items, totals, paymentMethods, provider, currency, locale, cartController },
|
|
1591
|
+
initialStatus: initialState,
|
|
1592
|
+
onEvent,
|
|
1593
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CheckoutContent, { devTools })
|
|
1594
|
+
}
|
|
1595
|
+
) }) }) });
|
|
1596
|
+
};
|
|
1597
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1598
|
+
0 && (module.exports = {
|
|
1599
|
+
Checkout,
|
|
1600
|
+
checkoutThemes,
|
|
1601
|
+
cn,
|
|
1602
|
+
createCartEditPlugin,
|
|
1603
|
+
createCssVars,
|
|
1604
|
+
createDiscountPlugin,
|
|
1605
|
+
darken,
|
|
1606
|
+
defaultTheme,
|
|
1607
|
+
formatNumber,
|
|
1608
|
+
generateBrandTheme,
|
|
1609
|
+
getContrastColor,
|
|
1610
|
+
getLuminance,
|
|
1611
|
+
hexToRgb,
|
|
1612
|
+
isDark,
|
|
1613
|
+
isPaymentStep,
|
|
1614
|
+
isReviewStep,
|
|
1615
|
+
lighten,
|
|
1616
|
+
mergeTheme,
|
|
1617
|
+
mix,
|
|
1618
|
+
resolveTheme,
|
|
1619
|
+
rgbToHex,
|
|
1620
|
+
useCheckout
|
|
1621
|
+
});
|
|
1622
|
+
//# sourceMappingURL=index.cjs.map
|