@ecomiq/storefront 2.1.4 → 3.0.1
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/LICENSE +21 -0
- package/README.md +377 -76
- package/dist/account-Dx4e3syi.js +258 -0
- package/dist/account-forms-AVk71ko7.js +39 -0
- package/dist/account-forms.d.ts +13 -0
- package/dist/account.d.ts +240 -0
- package/dist/address-defaults-BqjRhS82.js +14 -0
- package/dist/address-defaults.d.ts +11 -0
- package/dist/analytics/analytics-CG1Qagwl.js +148 -0
- package/dist/analytics/analytics.d.ts +1 -0
- package/dist/analytics/config-DJr-GRhs.js +62 -0
- package/dist/analytics/config.d.ts +22 -0
- package/dist/analytics/consent-B13rBpBs.js +57 -0
- package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
- package/dist/analytics/consent-banner.d.ts +20 -0
- package/dist/analytics/cookies-pLozllyB.js +100 -0
- package/dist/analytics/cookies.d.ts +6 -0
- package/dist/analytics/destinations-iECrlLIr.js +144 -0
- package/dist/analytics/events-Dt81tIp1.js +102 -0
- package/dist/analytics/events.d.ts +51 -0
- package/dist/analytics/tracker-p4yaoOyk.js +51 -0
- package/dist/api-cache-DsuIn5hX.js +159 -0
- package/dist/bank-accounts-DlEczQQ5.js +81 -0
- package/dist/bank-accounts.d.ts +4 -0
- package/dist/captcha-DAqaIx8m.js +117 -0
- package/dist/captcha.d.ts +34 -0
- package/dist/cart-D7DxZ2lD.js +30 -0
- package/dist/cart-actions-Bsub_FFG.js +116 -0
- package/dist/cart-actions.d.ts +15 -0
- package/dist/cart-drawer-B7PPsR69.js +22 -0
- package/dist/cart-drawer.d.ts +9 -0
- package/dist/cart-form-y9P1r_EX.js +81 -0
- package/dist/cart-form.d.ts +24 -0
- package/dist/cart-summary-ur2VWKr-.js +89 -0
- package/dist/cart-summary.d.ts +31 -0
- package/dist/cart.d.ts +4 -0
- package/dist/catalog-7gJ-l5hW.js +67 -0
- package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
- package/dist/catalog-hooks.d.ts +111 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/checkout-DVWSXANi.js +474 -0
- package/dist/checkout-form-CbOIdENC.js +131 -0
- package/dist/checkout-form.d.ts +20 -0
- package/dist/checkout.d.ts +86 -0
- package/dist/complaint-book-BvwLvfx0.js +117 -0
- package/dist/complaint-book.d.ts +62 -0
- package/dist/culqi-authentication-BGr20vAJ.js +204 -0
- package/dist/culqi-authentication.d.ts +15 -0
- package/dist/culqi-checkout-CTXpKgp_.js +55 -0
- package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
- package/dist/culqi-three-ds.d.ts +29 -0
- package/dist/customer-avatar-CJXSDroK.js +12 -0
- package/dist/customer-avatar.d.ts +5 -0
- package/dist/customer-cookies-DHOgFAMV.js +45 -0
- package/dist/customer-csrf-C1oDX-Py.js +17 -0
- package/dist/customer-csrf.d.ts +3 -0
- package/dist/customer-document-fields-C1K-_DAD.js +70 -0
- package/dist/customer-document-fields.d.ts +9 -0
- package/dist/customer-login-form-C7AC6vXd.js +144 -0
- package/dist/customer-login-form.d.ts +17 -0
- package/dist/customer-registration-form-BEm_NEfP.js +93 -0
- package/dist/customer-registration-form.d.ts +8 -0
- package/dist/document-url-CGsAl5Tf.js +9 -0
- package/dist/documents-T1mzc_j8.js +38 -0
- package/dist/documents.d.ts +16 -0
- package/dist/entries/account.d.ts +11 -0
- package/dist/entries/account.js +10 -0
- package/dist/entries/analytics.d.ts +6 -0
- package/dist/entries/analytics.js +5 -0
- package/dist/entries/cart.d.ts +8 -0
- package/dist/entries/cart.js +7 -0
- package/dist/entries/checkout.d.ts +12 -0
- package/dist/entries/checkout.js +11 -0
- package/dist/entries/order.d.ts +11 -0
- package/dist/entries/order.js +11 -0
- package/dist/favicon-YpyhVJ9H.js +28 -0
- package/dist/favicon.d.ts +4 -0
- package/dist/form-BtQcbXlJ.js +18 -0
- package/dist/form-contracts-BNpHaTGJ.js +166 -0
- package/dist/form-contracts.d.ts +42 -0
- package/dist/form.d.ts +3 -0
- package/dist/hidden-CCWtOWKR.js +3 -0
- package/dist/hidden.d.ts +4 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/invoice-fields-BG0HjL6T.js +103 -0
- package/dist/invoice-fields.d.ts +39 -0
- package/dist/links-kFxBZfdb.js +96 -0
- package/dist/links.d.ts +21 -0
- package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
- package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
- package/dist/mercadopago-dialog.d.ts +21 -0
- package/dist/money-DtGOtDri.js +32 -0
- package/dist/money.d.ts +1 -0
- package/dist/optimistic-cart-BveVOHuh.js +53 -0
- package/dist/optimistic-cart.d.ts +16 -0
- package/dist/order-display-DTt5WBbc.js +112 -0
- package/dist/order-display.d.ts +20 -0
- package/dist/order-draft-DxV1Uhm3.js +38 -0
- package/dist/order-draft.d.ts +16 -0
- package/dist/order-pages-Dm6l5m0m.js +38 -0
- package/dist/order-pages.d.ts +12 -0
- package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
- package/dist/order-payment-continuation.d.ts +10 -0
- package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
- package/dist/order-payment-in-review.d.ts +5 -0
- package/dist/order-status-BJXLJwxq.js +162 -0
- package/dist/order-status.d.ts +89 -0
- package/dist/order-view-9IlBg5AF.js +62 -0
- package/dist/order-view.d.ts +12 -0
- package/dist/page-action-Ctkf4xC6.js +7 -0
- package/dist/page-seo-a5FJl8x5.js +262 -0
- package/dist/page-seo.d.ts +34 -0
- package/dist/pages-Ca_Gt6mq.js +150 -0
- package/dist/pages.d.ts +45 -0
- package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
- package/dist/payment-logo-BoPaGJc_.js +46 -0
- package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
- package/dist/payment-logo.d.ts +11 -0
- package/dist/payment-logos-BYvdwYyJ.js +23 -0
- package/dist/payment-methods-B8ocTgFZ.js +73 -0
- package/dist/payment-methods.d.ts +58 -0
- package/dist/payment-return-5cjypwIw.js +7 -0
- package/dist/place-CmW4hBWY.js +13 -0
- package/dist/place.d.ts +4 -0
- package/dist/preview-BiIBif3C.js +427 -0
- package/dist/preview-address-Cf0sJ-9p.js +21 -0
- package/dist/preview-cart-Dg2CVktS.js +376 -0
- package/dist/product-preview-BspMAecJ.js +51 -0
- package/dist/product-preview.d.ts +5 -0
- package/dist/product-variants-BNDHvs1y.js +46 -0
- package/dist/product-variants.d.ts +12 -0
- package/dist/public-pages-XtwlaLwt.js +194 -0
- package/dist/public-pages.d.ts +67 -0
- package/dist/registration-CpDcv8ql.js +91 -0
- package/dist/registration.d.ts +12 -0
- package/dist/request-security-CRsRMgwj.js +76 -0
- package/dist/rich-text-M-Ju8RL4.js +9 -0
- package/dist/rich-text.d.ts +5 -0
- package/dist/route-text-DcJw2SsK.js +9 -0
- package/dist/route-text.d.ts +4 -0
- package/dist/section-preview-CmsMl3yu.js +83 -0
- package/dist/section-preview.d.ts +12 -0
- package/dist/sections/account-addresses.d.ts +18 -0
- package/dist/sections/account-addresses.js +375 -0
- package/dist/sections/account-order.d.ts +12 -0
- package/dist/sections/account-order.js +592 -0
- package/dist/sections/account-orders.d.ts +8 -0
- package/dist/sections/account-orders.js +359 -0
- package/dist/sections/account-profile.d.ts +16 -0
- package/dist/sections/account-profile.js +123 -0
- package/dist/sections/checkout.d.ts +24 -0
- package/dist/sections/checkout.js +963 -0
- package/dist/sections/complaint-book.d.ts +8 -0
- package/dist/sections/complaint-book.js +538 -0
- package/dist/sections/design-system.d.ts +1 -0
- package/dist/sections/design-system.js +310 -0
- package/dist/sections/draft-invoice.d.ts +20 -0
- package/dist/sections/draft-invoice.js +158 -0
- package/dist/sections/order-tracking.d.ts +19 -0
- package/dist/sections/order-tracking.js +141 -0
- package/dist/sections-CNUMWTF_.js +77 -0
- package/dist/sections.d.ts +19 -0
- package/dist/seo-GpDRhAAA.js +78 -0
- package/dist/session-CKvmFJyg.js +379 -0
- package/dist/session.d.ts +24 -0
- package/dist/settings-B0NO2A6Y.js +78 -0
- package/dist/settings.d.ts +51 -0
- package/dist/source-marker-DhqSkW7n.js +4 -0
- package/dist/sources-B-3JFJfb.js +46 -0
- package/dist/sources.d.ts +7 -0
- package/dist/store-8Q-_LFrQ.js +152 -0
- package/dist/store-contact--hY6CoFb.js +19 -0
- package/dist/store-contact.d.ts +6 -0
- package/dist/store.d.ts +17 -0
- package/dist/storefront-language-DrMjVbmk.js +29 -0
- package/dist/storefront-language.d.ts +14 -0
- package/dist/storefront-text-D25ae1qf.js +11 -0
- package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
- package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
- package/dist/storefront-text.d.ts +4 -0
- package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
- package/dist/stripe-dialog.d.ts +30 -0
- package/dist/telemetry-HuZXWBPi.js +114 -0
- package/dist/telemetry.d.ts +10 -0
- package/dist/templates-CAqoNyTW.js +40 -0
- package/dist/templates.d.ts +15 -0
- package/dist/urls-CpGQPxvf.js +71 -0
- package/dist/urls.d.ts +18 -0
- package/dist/use-checkout-CZcxXJXi.js +42 -0
- package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
- package/dist/use-checkout-payment.d.ts +18 -0
- package/dist/use-checkout.d.ts +36 -0
- package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
- package/dist/use-order-payment-BKIz_IO0.js +45 -0
- package/dist/use-order-payment.d.ts +18 -0
- package/dist/use-payment-token-CErx-tN-.js +105 -0
- package/dist/vite-payment-logos-kziu7wKb.js +27 -0
- package/dist/vite-payment-logos.d.ts +24 -0
- package/dist/vite-public-assets-DW2fgtVb.js +209 -0
- package/dist/vite-public-assets.d.ts +29 -0
- package/dist/vite-sections-DTBr0-Kg.js +464 -0
- package/dist/vite-sections.d.ts +21 -0
- package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
- package/dist/vite-theme-ui.d.ts +10 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +127 -0
- package/dist/voucher-payment-CVFDZ1L8.js +66 -0
- package/dist/worker-CNi-gzCu.js +86 -0
- package/dist/worker.d.ts +27 -0
- package/package.json +100 -76
- package/src/sections/account-addresses.tsx +215 -0
- package/src/sections/account-order.tsx +424 -0
- package/src/sections/account-orders.tsx +259 -0
- package/src/sections/account-profile.tsx +70 -0
- package/src/sections/checkout.tsx +586 -0
- package/src/sections/complaint-book.tsx +325 -0
- package/src/sections/design-system.tsx +146 -0
- package/src/sections/draft-invoice.tsx +105 -0
- package/src/sections/order-tracking.tsx +81 -0
- package/src/vite-client.d.ts +4 -0
- package/bin/ecomiq.mjs +0 -179
- package/public/design-bridge.js +0 -318
- package/src/api-proxy.ts +0 -194
- package/src/auth/create-storefront-auth.ts +0 -94
- package/src/auth/ecomiq-oauth-password-client.ts +0 -26
- package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
- package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
- package/src/auth/ecomiq-oauth-password-types.ts +0 -24
- package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
- package/src/auth/env.ts +0 -7
- package/src/auth/secure-cookies.ts +0 -23
- package/src/auth/storefront-auth-base-url.ts +0 -117
- package/src/auth/storefront-auth.ts +0 -7
- package/src/cart-session.ts +0 -239
- package/src/client.ts +0 -48
- package/src/codegen.mjs +0 -801
- package/src/components/account/customer-addresses-panel.tsx +0 -678
- package/src/components/account/customer-order-detail-panel.tsx +0 -285
- package/src/components/account/customer-orders-panel.tsx +0 -192
- package/src/components/account/customer-profile-panel.tsx +0 -379
- package/src/components/checkout/cart-sync.ts +0 -143
- package/src/components/checkout/checkout-contact-section.tsx +0 -76
- package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
- package/src/components/checkout/checkout-form-controls.tsx +0 -139
- package/src/components/checkout/checkout-line-items.tsx +0 -43
- package/src/components/checkout/checkout-order-summary.tsx +0 -165
- package/src/components/checkout/checkout-payment-section.tsx +0 -212
- package/src/components/checkout/checkout-session-view.tsx +0 -564
- package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
- package/src/components/checkout/checkout-shipping-section.tsx +0 -98
- package/src/components/checkout/checkout-states.tsx +0 -73
- package/src/components/checkout/checkout-step-card.tsx +0 -101
- package/src/components/checkout/checkout-steps.ts +0 -111
- package/src/components/checkout/form-schema.ts +0 -281
- package/src/components/checkout/format.ts +0 -15
- package/src/components/checkout/index.ts +0 -5
- package/src/components/checkout/map-customer-to-checkout.ts +0 -188
- package/src/components/checkout/types.ts +0 -41
- package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
- package/src/components/checkout/use-checkout-geo.ts +0 -50
- package/src/components/checkout/use-checkout-session.ts +0 -640
- package/src/components/hero.tsx +0 -291
- package/src/config.d.ts +0 -94
- package/src/config.mjs +0 -146
- package/src/format-money.ts +0 -31
- package/src/health.ts +0 -90
- package/src/hooks/index.ts +0 -9
- package/src/hooks/recently-viewed.ts +0 -125
- package/src/index.ts +0 -349
- package/src/link.tsx +0 -2
- package/src/pages/account/account-page-skeleton.tsx +0 -69
- package/src/pages/account/account-page-view.tsx +0 -270
- package/src/pages/account/account-shell.tsx +0 -101
- package/src/pages/account/parse-account-route.ts +0 -36
- package/src/pages/account.tsx +0 -16
- package/src/pages/api.$.ts +0 -20
- package/src/pages/api.auth.$.ts +0 -16
- package/src/pages/checkout.$checkoutId.tsx +0 -60
- package/src/pages/checkout.tsx +0 -31
- package/src/pages/pages.$pageSlug.tsx +0 -74
- package/src/pages/thank-you.tsx +0 -53
- package/src/providers/auth.tsx +0 -96
- package/src/providers/cart.tsx +0 -275
- package/src/providers/ecomiq.ts +0 -137
- package/src/providers/index.ts +0 -26
- package/src/proxy-api.ts +0 -20
- package/src/public-config.ts +0 -24
- package/src/review-submission.ts +0 -89
- package/src/runtime/context.ts +0 -2
- package/src/runtime/default-layout.tsx +0 -64
- package/src/runtime/head.ts +0 -149
- package/src/runtime/page.tsx +0 -143
- package/src/runtime/postcss-browser-stub.mjs +0 -5
- package/src/server-config-store.ts +0 -35
- package/src/server.ts +0 -286
- package/src/styles.css +0 -76
- package/src/ui/badge.tsx +0 -32
- package/src/ui/button.tsx +0 -31
- package/src/ui/card.tsx +0 -31
- package/src/ui/carousel.tsx +0 -218
- package/src/ui/cn.ts +0 -6
- package/src/ui/index.ts +0 -8
- package/src/ui/separator.tsx +0 -6
- package/src/ui/skeleton.tsx +0 -6
- package/src/ui/storefront-error.tsx +0 -49
- package/src/utils.ts +0 -36
- package/src/vite-inline.mjs +0 -263
- package/templates/__root.tsx +0 -83
- package/templates/router.tsx +0 -19
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { RouterContextProvider } from "react-router";
|
|
2
|
+
import { PublicOrderTracking } from "@ecomiq/core";
|
|
3
|
+
export type GeoPlace = {
|
|
4
|
+
code: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export type ReviewActionData = {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
error: string | null;
|
|
10
|
+
};
|
|
11
|
+
export declare function loadGeo(request: Request, context: Readonly<RouterContextProvider>): Promise<{
|
|
12
|
+
departments: GeoPlace[];
|
|
13
|
+
provinces: GeoPlace[];
|
|
14
|
+
districts: GeoPlace[];
|
|
15
|
+
}>;
|
|
16
|
+
export declare function loadSearchSuggestions(request: Request, context: Readonly<RouterContextProvider>): Promise<{
|
|
17
|
+
q: string;
|
|
18
|
+
products: never[] | {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
slug: string;
|
|
22
|
+
url: string;
|
|
23
|
+
imageUrl: string;
|
|
24
|
+
}[];
|
|
25
|
+
}>;
|
|
26
|
+
export type TrackingData = {
|
|
27
|
+
code: string;
|
|
28
|
+
tracking: PublicOrderTracking | null;
|
|
29
|
+
missing: boolean;
|
|
30
|
+
language: string;
|
|
31
|
+
timeZone: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function loadTracking(request: Request, context: Readonly<RouterContextProvider>, code: string | undefined): Promise<TrackingData>;
|
|
34
|
+
export declare function loadReview(context: Readonly<RouterContextProvider>, token: string): Promise<{
|
|
35
|
+
review: {
|
|
36
|
+
orderId: string;
|
|
37
|
+
orderNumber: string;
|
|
38
|
+
customerName: string | null;
|
|
39
|
+
email: string | null;
|
|
40
|
+
deliveredAt: string | null;
|
|
41
|
+
items: {
|
|
42
|
+
productId: string;
|
|
43
|
+
title: string;
|
|
44
|
+
variantTitle: string | null;
|
|
45
|
+
thumbnail: string | null;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
export declare function reviewAction(request: Request, context: Readonly<RouterContextProvider>, token: string): Promise<import("react-router").UNSAFE_DataWithResponseInit<ReviewActionData> | {
|
|
50
|
+
ok: true;
|
|
51
|
+
error: null;
|
|
52
|
+
}>;
|
|
53
|
+
export declare function productReviewAction(request: Request, context: Readonly<RouterContextProvider>): Promise<import("react-router").UNSAFE_DataWithResponseInit<ReviewActionData> | {
|
|
54
|
+
ok: true;
|
|
55
|
+
error: null;
|
|
56
|
+
}>;
|
|
57
|
+
export type StorefrontPageData = {
|
|
58
|
+
title: string;
|
|
59
|
+
html: string;
|
|
60
|
+
layout: string;
|
|
61
|
+
seo: {
|
|
62
|
+
title: string;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export declare function readPage(context: Readonly<RouterContextProvider>, handle: string): Promise<StorefrontPageData | null>;
|
|
67
|
+
export declare function loadPage(context: Readonly<RouterContextProvider>, handle: string): Promise<StorefrontPageData>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { safeNext, text } from "./form-BtQcbXlJ.js";
|
|
2
|
+
import { rejectCrossOriginAction, rejectCustomerCsrf } from "./request-security-CRsRMgwj.js";
|
|
3
|
+
import { customer, customerHeaders } from "./session-CKvmFJyg.js";
|
|
4
|
+
import { store } from "./store-8Q-_LFrQ.js";
|
|
5
|
+
import { worker } from "./worker-CNi-gzCu.js";
|
|
6
|
+
import { data, redirect } from "react-router";
|
|
7
|
+
import { ApiError, CustomerRegistrationInputSchema } from "@ecomiq/core";
|
|
8
|
+
// src/registration.ts
|
|
9
|
+
const PRIVATE_HEADERS = {
|
|
10
|
+
"Cache-Control": "private, no-store",
|
|
11
|
+
"Referrer-Policy": "no-referrer"
|
|
12
|
+
};
|
|
13
|
+
const FIELDS = [
|
|
14
|
+
"firstName",
|
|
15
|
+
"lastName",
|
|
16
|
+
"phone",
|
|
17
|
+
"dateOfBirth"
|
|
18
|
+
];
|
|
19
|
+
async function registrationToday(context) {
|
|
20
|
+
const settings = await store(context).getSettings();
|
|
21
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
22
|
+
timeZone: settings.store.timeZone,
|
|
23
|
+
year: "numeric",
|
|
24
|
+
month: "2-digit",
|
|
25
|
+
day: "2-digit"
|
|
26
|
+
}).format(/* @__PURE__ */ new Date());
|
|
27
|
+
}
|
|
28
|
+
async function registrationData(registration, next, context, error = null, status = 200) {
|
|
29
|
+
const settings = await store(context).getSettings();
|
|
30
|
+
return data({
|
|
31
|
+
registration,
|
|
32
|
+
next,
|
|
33
|
+
storeName: settings.store.name,
|
|
34
|
+
error,
|
|
35
|
+
today: await registrationToday(context)
|
|
36
|
+
}, {
|
|
37
|
+
status,
|
|
38
|
+
headers: PRIVATE_HEADERS
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** The session exists at this stage; profile completion must not guard its own route. */
|
|
42
|
+
async function loadCustomerRegistration(request, context) {
|
|
43
|
+
const next = safeNext(new URL(request.url).searchParams.get("next"));
|
|
44
|
+
const current = await customer(request, context);
|
|
45
|
+
if (!current) return redirect(`/account/login?next=${encodeURIComponent(next)}`, { headers: PRIVATE_HEADERS });
|
|
46
|
+
const registration = await store(context).getCustomerRegistration(customerHeaders(current));
|
|
47
|
+
if (!registration.requiresCompletion) return redirect(next, { headers: PRIVATE_HEADERS });
|
|
48
|
+
return registrationData(registration, next, context);
|
|
49
|
+
}
|
|
50
|
+
async function submitCustomerRegistration(request, context) {
|
|
51
|
+
const rejected = rejectCrossOriginAction(request) ?? await rejectCustomerCsrf(request, context.get(worker).env);
|
|
52
|
+
if (rejected) return rejected;
|
|
53
|
+
const next = safeNext(new URL(request.url).searchParams.get("next"));
|
|
54
|
+
const current = await customer(request, context);
|
|
55
|
+
if (!current) return redirect(`/account/login?next=${encodeURIComponent(next)}`, {
|
|
56
|
+
status: 303,
|
|
57
|
+
headers: PRIVATE_HEADERS
|
|
58
|
+
});
|
|
59
|
+
const client = store(context);
|
|
60
|
+
const options = customerHeaders(current);
|
|
61
|
+
const registration = await client.getCustomerRegistration(options);
|
|
62
|
+
if (!registration.requiresCompletion) return redirect(next, {
|
|
63
|
+
status: 303,
|
|
64
|
+
headers: PRIVATE_HEADERS
|
|
65
|
+
});
|
|
66
|
+
const form = await request.formData();
|
|
67
|
+
const skip = text(form, "intent") === "skip";
|
|
68
|
+
const input = {};
|
|
69
|
+
for (const field of FIELDS) if (!skip && registration.fields[field] !== "hidden") input[field] = text(form, field) || null;
|
|
70
|
+
const submitted = {
|
|
71
|
+
...registration,
|
|
72
|
+
...input
|
|
73
|
+
};
|
|
74
|
+
const parsed = CustomerRegistrationInputSchema.safeParse(input);
|
|
75
|
+
if (!parsed.success) return registrationData(submitted, next, context, "Revisa los datos ingresados y vuelve a intentar.", 400);
|
|
76
|
+
for (const field of FIELDS) if (registration.fields[field] === "required" && !submitted[field]?.trim()) return registrationData(submitted, next, context, "Completa los datos obligatorios para continuar.", 400);
|
|
77
|
+
if (input.dateOfBirth && input.dateOfBirth > await registrationToday(context)) return registrationData(submitted, next, context, "La fecha de nacimiento no puede ser futura.", 400);
|
|
78
|
+
try {
|
|
79
|
+
const completed = await client.completeCustomerRegistration(parsed.data, options);
|
|
80
|
+
if (completed.requiresCompletion) return registrationData(completed, next, context, "Completa los datos obligatorios para continuar.", 400);
|
|
81
|
+
return redirect(next, {
|
|
82
|
+
status: 303,
|
|
83
|
+
headers: PRIVATE_HEADERS
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
let message = "No pudimos guardar tus datos. Intenta de nuevo.";
|
|
87
|
+
if (error instanceof ApiError && error.status === 400) message = error.message;
|
|
88
|
+
return registrationData(submitted, next, context, message, 400);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export { loadCustomerRegistration, submitCustomerRegistration };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RouterContextProvider, data } from "react-router";
|
|
2
|
+
import { CustomerRegistration } from "@ecomiq/core";
|
|
3
|
+
export type CustomerRegistrationData = {
|
|
4
|
+
registration: CustomerRegistration;
|
|
5
|
+
storeName: string;
|
|
6
|
+
next: string;
|
|
7
|
+
error: string | null;
|
|
8
|
+
today: string;
|
|
9
|
+
};
|
|
10
|
+
/** The session exists at this stage; profile completion must not guard its own route. */
|
|
11
|
+
export declare function loadCustomerRegistration(request: Request, context: Readonly<RouterContextProvider>): Promise<Response | ReturnType<typeof data<CustomerRegistrationData>>>;
|
|
12
|
+
export declare function submitCustomerRegistration(request: Request, context: Readonly<RouterContextProvider>): Promise<Response | ReturnType<typeof data<CustomerRegistrationData>>>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { base64Url, base64UrlBytes, customerSessionId, equalSecret } from "./customer-cookies-DHOgFAMV.js";
|
|
2
|
+
import { PRIVATE_STOREFRONT_SUBTREES } from "@ecomiq/core/redirects";
|
|
3
|
+
// src/request-security.ts
|
|
4
|
+
const SAFE_METHODS = /* @__PURE__ */ new Set([
|
|
5
|
+
"GET",
|
|
6
|
+
"HEAD",
|
|
7
|
+
"OPTIONS"
|
|
8
|
+
]);
|
|
9
|
+
const PRIVATE_PATH = new RegExp(`^/(${PRIVATE_STOREFRONT_SUBTREES.join("|")})(/|$)`);
|
|
10
|
+
async function sessionCsrfToken(sessionId, env) {
|
|
11
|
+
const encoder = new TextEncoder();
|
|
12
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(env.SESSION_SECRET), {
|
|
13
|
+
name: "HMAC",
|
|
14
|
+
hash: "SHA-256"
|
|
15
|
+
}, false, ["sign"]);
|
|
16
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(`customer-csrf\n${env.ECOMIQ_STORE}\n${sessionId}`));
|
|
17
|
+
return base64Url(new Uint8Array(signature));
|
|
18
|
+
}
|
|
19
|
+
async function customerCsrfTokenForRequest(request, env) {
|
|
20
|
+
const sessionId = await customerSessionId(request, env);
|
|
21
|
+
return sessionId ? sessionCsrfToken(sessionId, env) : "";
|
|
22
|
+
}
|
|
23
|
+
async function rejectCustomerCsrf(request, env) {
|
|
24
|
+
if (SAFE_METHODS.has(request.method)) return null;
|
|
25
|
+
const sessionId = await customerSessionId(request, env);
|
|
26
|
+
if (!sessionId) return null;
|
|
27
|
+
let submitted = request.headers.get("X-Customer-Csrf");
|
|
28
|
+
if (!submitted) try {
|
|
29
|
+
submitted = (await request.clone().formData()).get("__customer_csrf");
|
|
30
|
+
} catch {
|
|
31
|
+
submitted = null;
|
|
32
|
+
}
|
|
33
|
+
const expected = await sessionCsrfToken(sessionId, env);
|
|
34
|
+
if (typeof submitted === "string" && equalSecret(submitted, expected)) return null;
|
|
35
|
+
return new Response("La sesión cambió. Recarga la página antes de continuar.", {
|
|
36
|
+
status: 403,
|
|
37
|
+
headers: {
|
|
38
|
+
"Cache-Control": "private, no-store",
|
|
39
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Browser actions must originate on the storefront that owns the cookies. */
|
|
44
|
+
function rejectCrossOriginAction(request) {
|
|
45
|
+
if (SAFE_METHODS.has(request.method)) return null;
|
|
46
|
+
if (request.headers.get("Origin") === new URL(request.url).origin && request.headers.get("Sec-Fetch-Site") !== "cross-site") return null;
|
|
47
|
+
return new Response("La solicitud debe enviarse desde esta tienda. Recarga la página e intenta de nuevo.", {
|
|
48
|
+
status: 403,
|
|
49
|
+
headers: {
|
|
50
|
+
"Cache-Control": "private, no-store",
|
|
51
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function privateCustomerResponse(request) {
|
|
56
|
+
const path = new URL(request.url).pathname;
|
|
57
|
+
return !SAFE_METHODS.has(request.method) || PRIVATE_PATH.test(path) || /(?:^|;\s*)(?:__Host-ecomiq-customer|cart)=/.test(request.headers.get("Cookie") ?? "");
|
|
58
|
+
}
|
|
59
|
+
async function customerClientHeaders(request, env, target) {
|
|
60
|
+
const address = request.headers.get("CF-Connecting-IP");
|
|
61
|
+
const store = env.ECOMIQ_STORE_ID;
|
|
62
|
+
if (!address || !store || !env.CUSTOMER_IP_KEY) return {};
|
|
63
|
+
const timestamp = String(Math.floor(Date.now() / 1e3));
|
|
64
|
+
const key = await crypto.subtle.importKey("raw", base64UrlBytes(env.CUSTOMER_IP_KEY), {
|
|
65
|
+
name: "HMAC",
|
|
66
|
+
hash: "SHA-256"
|
|
67
|
+
}, false, ["sign"]);
|
|
68
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${timestamp}\n${store}\n${address}\nPOST\n${target.pathname}`));
|
|
69
|
+
return {
|
|
70
|
+
"X-Ecomiq-Client-Ip": address,
|
|
71
|
+
"X-Ecomiq-Client-Store": store,
|
|
72
|
+
"X-Ecomiq-Client-Timestamp": timestamp,
|
|
73
|
+
"X-Ecomiq-Client-Signature": base64Url(new Uint8Array(signature))
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export { customerClientHeaders, customerCsrfTokenForRequest, privateCustomerResponse, rejectCrossOriginAction, rejectCustomerCsrf };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { storefrontText } from "./storefront-text-D25ae1qf.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// src/route-text.ts
|
|
4
|
+
const RootLanguage = z.object({ shop: z.object({ translations: z.record(z.string(), z.string()).default({}) }) });
|
|
5
|
+
function storefrontRouteText(matches, source) {
|
|
6
|
+
const root = RootLanguage.safeParse(matches.find((match) => match?.id === "root")?.data);
|
|
7
|
+
return storefrontText(root.success ? root.data.shop.translations : {}, source);
|
|
8
|
+
}
|
|
9
|
+
export { storefrontRouteText };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { loadSections, withSections } from "./sections-CNUMWTF_.js";
|
|
2
|
+
import { worker } from "./worker-CNi-gzCu.js";
|
|
3
|
+
import { data, useLoaderData } from "react-router";
|
|
4
|
+
import { createElement, useState } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
// src/section-preview.tsx
|
|
7
|
+
const COMPONENT = /^[A-Z][A-Za-z0-9]*$/;
|
|
8
|
+
const DESIGN = {
|
|
9
|
+
file: "@ecomiq/storefront/sections/design-system",
|
|
10
|
+
component: "DesignSystem"
|
|
11
|
+
};
|
|
12
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
13
|
+
function notFound() {
|
|
14
|
+
throw data(null, { status: 404 });
|
|
15
|
+
}
|
|
16
|
+
function sectionFile(path) {
|
|
17
|
+
return `app/${path.replace(/^(?:\.\.\/)+/, "")}`;
|
|
18
|
+
}
|
|
19
|
+
function sectionEntries(module) {
|
|
20
|
+
const load = module.load;
|
|
21
|
+
if (typeof load !== "function") return [];
|
|
22
|
+
const section = load.section;
|
|
23
|
+
if (typeof section !== "string") return [];
|
|
24
|
+
return [{
|
|
25
|
+
section,
|
|
26
|
+
load,
|
|
27
|
+
props: {}
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
async function sectionPreview(args, modules) {
|
|
31
|
+
const { env } = args.context.get(worker);
|
|
32
|
+
if (env.ECOMIQ_PREVIEW !== "1") notFound();
|
|
33
|
+
const url = new URL(args.request.url);
|
|
34
|
+
if (url.searchParams.get("design") === "1") {
|
|
35
|
+
const design = await import(
|
|
36
|
+
/* @vite-ignore */
|
|
37
|
+
DESIGN.file
|
|
38
|
+
);
|
|
39
|
+
rendered.set(DESIGN.file, design);
|
|
40
|
+
return DESIGN;
|
|
41
|
+
}
|
|
42
|
+
const file = url.searchParams.get("file") ?? "";
|
|
43
|
+
const component = url.searchParams.get("component") ?? "";
|
|
44
|
+
const path = Object.keys(modules).find((candidate) => sectionFile(candidate) === file);
|
|
45
|
+
const importer = path === void 0 ? void 0 : modules[path];
|
|
46
|
+
if (importer === void 0 || !COMPONENT.test(component)) notFound();
|
|
47
|
+
const module = await importer();
|
|
48
|
+
if (typeof module[component] !== "function") notFound();
|
|
49
|
+
rendered.set(file, module);
|
|
50
|
+
const pending = loadSections(args, sectionEntries(module));
|
|
51
|
+
const sections = Object.fromEntries(await Promise.all(Object.entries(pending).map(async ([key, value]) => [key, await value])));
|
|
52
|
+
return withSections({
|
|
53
|
+
file,
|
|
54
|
+
component
|
|
55
|
+
}, sections);
|
|
56
|
+
}
|
|
57
|
+
function sectionPreviewMeta() {
|
|
58
|
+
return [{
|
|
59
|
+
name: "robots",
|
|
60
|
+
content: "noindex"
|
|
61
|
+
}];
|
|
62
|
+
}
|
|
63
|
+
function serverHtml(component) {
|
|
64
|
+
if (typeof document === "undefined") return "";
|
|
65
|
+
return document.querySelector(`[data-ecomiq-preview="${CSS.escape(component)}"]`)?.innerHTML ?? "";
|
|
66
|
+
}
|
|
67
|
+
function SectionPreview() {
|
|
68
|
+
const { file, component } = useLoaderData();
|
|
69
|
+
const [html] = useState(() => serverHtml(component));
|
|
70
|
+
const Section = rendered.get(file)?.[component];
|
|
71
|
+
if (typeof Section !== "function") return /* @__PURE__ */ jsx("div", {
|
|
72
|
+
"data-ecomiq-preview": component,
|
|
73
|
+
style: { display: "flow-root" },
|
|
74
|
+
suppressHydrationWarning: true,
|
|
75
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
76
|
+
});
|
|
77
|
+
return /* @__PURE__ */ jsx("div", {
|
|
78
|
+
"data-ecomiq-preview": component,
|
|
79
|
+
style: { display: "flow-root" },
|
|
80
|
+
children: createElement(Section)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export { SectionPreview, sectionPreview, sectionPreviewMeta };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LoaderFunctionArgs, MetaDescriptor } from "react-router";
|
|
2
|
+
import { ReactElement } from "react";
|
|
3
|
+
export type SectionModules = {
|
|
4
|
+
readonly [path: string]: () => Promise<unknown>;
|
|
5
|
+
};
|
|
6
|
+
export type SectionPreviewData = {
|
|
7
|
+
readonly file: string;
|
|
8
|
+
readonly component: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function sectionPreview(args: LoaderFunctionArgs, modules: SectionModules): Promise<SectionPreviewData>;
|
|
11
|
+
export declare function sectionPreviewMeta(): MetaDescriptor[];
|
|
12
|
+
export declare function SectionPreview(): ReactElement;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AddressActionData } from "../account.js";
|
|
2
|
+
import "../index.js";
|
|
3
|
+
import { SectionCatalog } from "../settings.js";
|
|
4
|
+
import "../entries/account.js";
|
|
5
|
+
import { CustomerAddress } from "@ecomiq/core";
|
|
6
|
+
export declare const catalog: SectionCatalog;
|
|
7
|
+
type Country = {
|
|
8
|
+
code: string;
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function AccountAddresses({ loaderData, actionData }: {
|
|
12
|
+
loaderData: {
|
|
13
|
+
addresses: CustomerAddress[];
|
|
14
|
+
countries: Country[];
|
|
15
|
+
};
|
|
16
|
+
actionData?: AddressActionData;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
export {};
|