@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/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