@fayz-ai/storefront 0.63.0 → 0.65.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.
@@ -7,7 +7,7 @@ import { setShopCarrierQuotesResolver, setShopAccessTokenResolver, setShopShippi
7
7
  import { normalizePostalCode, getCountrySchema, lookupPostalCode, defaultRouterAdapter, defineLoader, staticRouterAdapter, isLoaderParamRef, isLoaderPropRef, resolveLoader, errorMessage, dataNeedKey, getBlockMeta, defaultPropsFromSettings, getBlockErrorVisibility, isBlockEnabledOn, checkBlockConstraints, checkBlockContext, repairBlockTree, renderBlocks, clearExtensionPoints, defineExtensionPoint, runExtensionPoint, runEscapeValve, breadcrumbJsonLd, hasEntityDeclaration, resolveEntityDeclaration, defineBlock, formatPostalCode, countryName, defineEntity, listEntityDeclarations, resolveHandle, seoOrigin, describeError, RESERVED_ENTITY_FIELDS, ENTITY_FIELD_TYPES, handleNameFor, registerHandle, validateAddress, hasHandle, listHandles, blockRegistry, loaderRegistry, listLoaders } from '@fayz-ai/core';
8
8
  import React38, { createContext, useContext, useEffect, useRef, useState, useSyncExternalStore, useMemo, useCallback } from 'react';
9
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
- import { isStorageImageUrl, imageVariantUrl, imageSrcSet, IMAGE_SRCSET_WIDTHS, validateDiscount, documentDigits, isChargeableProvider, supportsCard, formatDocument, gatewayLabel, isValidDocument, isValidCpf } from '@fayz-ai/shop';
10
+ import { isStorageImageUrl, imageVariantUrl, imageSrcSet, IMAGE_SRCSET_WIDTHS, validateDiscount, documentDigits, isChargeableProvider, supportsCard, formatDocument, isValidDocument, gatewayLabel, isValidCpf } from '@fayz-ai/shop';
11
11
  import { ChevronLeft, ChevronRight, ShoppingBag, X, Minus, Plus, Send, MessageCircle, Mail, Truck, RefreshCcw, ShieldCheck, ChevronDown, Check, Search, Menu, Trash2, MapPin, Lock, CreditCard, UserRound, Store, Bike, Clock, Pencil, Star, Phone, User, UserCircle, Package, LogOut, QrCode, Copy, XCircle, RotateCcw, PackageCheck, ChefHat, Info, AlertCircle } from 'lucide-react';
12
12
  import { getLucideIcon } from '@fayz-ai/ui/icons';
13
13
  import { storefrontDiscountToPromotion, previewCouponPercent, discountToPromotion, PROMOTION_TYPES, PROMOTION_STATUSES, APPLICATION_METHOD_TYPES, APPLICATION_METHOD_TARGET_TYPES, APPLICATION_METHOD_ALLOCATIONS, hasRuleAttribute, listRuleAttributeKeys, RULE_OPERATORS, resolveRuleAttribute } from '@fayz-ai/shop/rules';
@@ -925,6 +925,10 @@ var ptBR = {
925
925
  // Step 1 — contact
926
926
  "checkout.contact.title": "Informa\xE7\xF5es de contato",
927
927
  "checkout.contact.signedInAs": "Conectado como {{email}}",
928
+ "checkout.contact.edit": "Alterar",
929
+ "checkout.contact.notYou": "N\xE3o \xE9 voc\xEA?",
930
+ "checkout.contact.signOut": "Sair",
931
+ "checkout.contact.emailLocked": "O e-mail \xE9 o da sua conta.",
928
932
  "checkout.contact.haveAccount": "J\xE1 tem conta? Entrar",
929
933
  "checkout.contact.email": "E-mail",
930
934
  "checkout.contact.name": "Nome completo",
@@ -1071,6 +1075,10 @@ var en = {
1071
1075
  "checkout.steps.4": "Done",
1072
1076
  "checkout.contact.title": "Contact information",
1073
1077
  "checkout.contact.signedInAs": "Signed in as {{email}}",
1078
+ "checkout.contact.edit": "Change",
1079
+ "checkout.contact.notYou": "Not you?",
1080
+ "checkout.contact.signOut": "Sign out",
1081
+ "checkout.contact.emailLocked": "This is your account e-mail.",
1074
1082
  "checkout.contact.haveAccount": "Have an account? Sign in",
1075
1083
  "checkout.contact.email": "Email",
1076
1084
  "checkout.contact.name": "Full name",
@@ -1202,6 +1210,10 @@ var es = {
1202
1210
  "checkout.steps.4": "Listo",
1203
1211
  "checkout.contact.title": "Datos de contacto",
1204
1212
  "checkout.contact.signedInAs": "Has iniciado sesi\xF3n como {{email}}",
1213
+ "checkout.contact.edit": "Cambiar",
1214
+ "checkout.contact.notYou": "\xBFNo eres t\xFA?",
1215
+ "checkout.contact.signOut": "Salir",
1216
+ "checkout.contact.emailLocked": "Es el correo de tu cuenta.",
1205
1217
  "checkout.contact.haveAccount": "\xBFYa tienes cuenta? Entrar",
1206
1218
  "checkout.contact.email": "Email",
1207
1219
  "checkout.contact.name": "Nombre completo",
@@ -1340,8 +1352,8 @@ function resolveCountries(config) {
1340
1352
  }
1341
1353
  function resolveConfig(config) {
1342
1354
  const homePage = config.pages?.home ?? (config.home ? { blocks: config.home.sections } : void 0);
1343
- const catalogPath = homePage ? "/catalog" : "/";
1344
1355
  const commerceMode = config.commerceMode ?? "checkout";
1356
+ const catalogPath = commerceMode === "delivery" ? config.delivery?.menuPath ?? "/menu" : homePage ? "/catalog" : "/";
1345
1357
  const checkoutEnabled = commerceMode === "checkout" || commerceMode === "delivery";
1346
1358
  return {
1347
1359
  ...config,
@@ -9438,6 +9450,56 @@ function pickLastAddress(orders) {
9438
9450
  }
9439
9451
  return null;
9440
9452
  }
9453
+ function maskDocument(raw) {
9454
+ const d = documentDigits(raw);
9455
+ if (d.length === 11) return `***.${d.slice(3, 6)}.${d.slice(6, 9)}-**`;
9456
+ if (d.length === 14) return `${d.slice(0, 2)}.${d.slice(2, 5)}.${d.slice(5, 8)}/${d.slice(8, 12)}-${d.slice(12)}`;
9457
+ return raw;
9458
+ }
9459
+ function formatBrPhone(raw) {
9460
+ let d = raw.replace(/\D/g, "");
9461
+ if (d.startsWith("55") && (d.length === 12 || d.length === 13)) d = d.slice(2);
9462
+ if (d.length === 11) return `(${d.slice(0, 2)}) ${d.slice(2, 7)}-${d.slice(7)}`;
9463
+ if (d.length === 10) return `(${d.slice(0, 2)}) ${d.slice(2, 6)}-${d.slice(6)}`;
9464
+ return raw;
9465
+ }
9466
+ function ContactSummary({ name, email, phone, document: document2, onEdit, onSignOut, labels }) {
9467
+ const line = [phone ? formatBrPhone(phone) : null, document2 ? maskDocument(document2) : null].filter(Boolean).join(" \xB7 ");
9468
+ return /* @__PURE__ */ jsxs("div", { "data-testid": "checkout-contact-summary", className: "rounded-xl border bg-card p-4", children: [
9469
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
9470
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 grid h-6 w-6 shrink-0 place-items-center rounded-full bg-primary/15 text-primary", "aria-hidden": true, children: /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) }),
9471
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
9472
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold", children: name }),
9473
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-muted-foreground", children: email }),
9474
+ line && /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-muted-foreground", children: line })
9475
+ ] }),
9476
+ /* @__PURE__ */ jsx(
9477
+ "button",
9478
+ {
9479
+ type: "button",
9480
+ onClick: onEdit,
9481
+ "data-testid": "checkout-contact-edit",
9482
+ className: "shrink-0 text-sm font-medium text-primary hover:underline",
9483
+ children: labels.edit
9484
+ }
9485
+ )
9486
+ ] }),
9487
+ /* @__PURE__ */ jsxs("p", { className: "mt-3 border-t pt-3 text-xs text-muted-foreground", children: [
9488
+ labels.notYou,
9489
+ " ",
9490
+ /* @__PURE__ */ jsx(
9491
+ "button",
9492
+ {
9493
+ type: "button",
9494
+ onClick: onSignOut,
9495
+ "data-testid": "checkout-contact-sign-out",
9496
+ className: "font-medium text-foreground hover:underline",
9497
+ children: labels.signOut
9498
+ }
9499
+ )
9500
+ ] })
9501
+ ] });
9502
+ }
9441
9503
  function toShippingAddress(form, country) {
9442
9504
  const schema = getCountrySchema(country);
9443
9505
  const isBrazil = schema.iso2 === "BR";
@@ -9562,6 +9624,9 @@ function CheckoutPage() {
9562
9624
  }));
9563
9625
  }, [known.ready, known.phone, known.document, known.address]);
9564
9626
  const [step, setStep] = useState(resume ? 3 : 1);
9627
+ const [contactKnown, setContactKnown] = useState(false);
9628
+ const [editingContact, setEditingContact] = useState(false);
9629
+ const skippedContact = React38.useRef(false);
9565
9630
  const fulfillmentType = useDeliveryStore((s2) => s2.fulfillmentType);
9566
9631
  const scheduledFor = useDeliveryStore((s2) => s2.scheduledFor);
9567
9632
  const deliveryReference = useDeliveryStore((s2) => {
@@ -9747,22 +9812,30 @@ function CheckoutPage() {
9747
9812
  }, [session.customerId]);
9748
9813
  useEffect(() => {
9749
9814
  const customerId = session.customerId;
9815
+ setContactKnown(false);
9750
9816
  if (!customerId) return;
9751
9817
  const provider = getShopProvider();
9752
9818
  const getCustomer = provider.getCustomer?.bind(provider);
9753
- if (!getCustomer) return;
9819
+ const getMyProfile = provider.getMyProfile?.bind(provider);
9754
9820
  let cancelled = false;
9755
- void getCustomer(customerId).then((customer) => {
9756
- if (cancelled || !customer) return;
9821
+ const fill = (c) => {
9822
+ if (cancelled || !c) return;
9757
9823
  setForm((current) => ({
9758
9824
  ...current,
9759
- phone: current.phone || customer.phone || "",
9760
- document: current.document || customer.document || "",
9761
- email: current.email || customer.email || "",
9762
- name: current.name || [customer.firstName, customer.lastName].filter(Boolean).join(" ").trim()
9825
+ phone: current.phone || c.phone || "",
9826
+ document: current.document || (c.document ? formatDocument(c.document) : ""),
9827
+ email: current.email || c.email || "",
9828
+ name: current.name || [c.firstName, c.lastName].filter(Boolean).join(" ").trim()
9763
9829
  }));
9764
- }).catch(() => {
9765
- });
9830
+ };
9831
+ void (async () => {
9832
+ const profile = getMyProfile ? await getMyProfile().catch(() => null) : null;
9833
+ fill(profile);
9834
+ if (getCustomer && (!profile || !profile.phone || !profile.document)) {
9835
+ fill(await getCustomer(customerId).catch(() => null));
9836
+ }
9837
+ if (!cancelled) setContactKnown(true);
9838
+ })();
9766
9839
  return () => {
9767
9840
  cancelled = true;
9768
9841
  };
@@ -9856,6 +9929,25 @@ function CheckoutPage() {
9856
9929
  }
9857
9930
  return validateDocument();
9858
9931
  }
9932
+ function contactComplete() {
9933
+ if (!session.customerId) return false;
9934
+ if (!form.email.trim() || !/.+@.+\..+/.test(form.email)) return false;
9935
+ if (!form.name.trim()) return false;
9936
+ const phone = form.phone.replace(/\D/g, "");
9937
+ if (requires("phone") && phone.length < 10) return false;
9938
+ if (shows("phone") && !phone) return false;
9939
+ if (isBrazil) {
9940
+ if (requires("document") && !isValidDocument(form.document)) return false;
9941
+ if (shows("document") && !isValidDocument(form.document)) return false;
9942
+ }
9943
+ return true;
9944
+ }
9945
+ const contactAsCard = !editingContact && contactKnown && contactComplete();
9946
+ useEffect(() => {
9947
+ if (skippedContact.current || step !== 1 || !contactAsCard) return;
9948
+ skippedContact.current = true;
9949
+ setStep(isPickup ? 3 : 2);
9950
+ }, [contactAsCard, step]);
9859
9951
  function validateDocument() {
9860
9952
  if (isBrazil) {
9861
9953
  if (requires("document") && !isValidDocument(form.document)) {
@@ -9933,6 +10025,15 @@ function CheckoutPage() {
9933
10025
  }
9934
10026
  function advance() {
9935
10027
  if (step === 1 && validateContact()) {
10028
+ if (editingContact && session.customerId) {
10029
+ void getShopProvider().saveMyProfile?.({
10030
+ name: form.name.trim(),
10031
+ phone: form.phone.trim() || null,
10032
+ document: documentDigits(form.document) || null
10033
+ }).catch(() => {
10034
+ });
10035
+ setEditingContact(false);
10036
+ }
9936
10037
  void getShopProvider().saveCustomerContact?.({
9937
10038
  phone: form.phone.trim() || null,
9938
10039
  document: documentDigits(form.document) || null
@@ -10056,10 +10157,29 @@ function CheckoutPage() {
10056
10157
  }
10057
10158
  )
10058
10159
  ] }),
10059
- /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
10160
+ contactAsCard ? /* @__PURE__ */ jsx(
10161
+ ContactSummary,
10162
+ {
10163
+ name: form.name,
10164
+ email: form.email,
10165
+ phone: shows("phone") ? form.phone : void 0,
10166
+ document: shows("document") ? form.document : void 0,
10167
+ onEdit: () => setEditingContact(true),
10168
+ onSignOut: () => {
10169
+ void signOutCustomer().finally(() => {
10170
+ setForm((current) => ({ ...current, email: "", name: "", phone: "", document: "" }));
10171
+ setEditingContact(false);
10172
+ });
10173
+ },
10174
+ labels: { edit: t("checkout.contact.edit"), notYou: t("checkout.contact.notYou"), signOut: t("checkout.contact.signOut") }
10175
+ }
10176
+ ) : /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
10060
10177
  checkoutField(t("checkout.contact.email"), TID.checkoutEmail, form.email, set("email"), {
10061
10178
  type: "email",
10062
- placeholder: t("checkout.contact.email")
10179
+ placeholder: t("checkout.contact.email"),
10180
+ // The account's address: an order under another e-mail would
10181
+ // not show up in this buyer's «Meus pedidos».
10182
+ ...session.customerId && session.email ? { readOnly: true, title: t("checkout.contact.emailLocked"), className: "w-full rounded-lg border bg-muted/40 px-3.5 py-3 text-sm text-muted-foreground outline-none" } : {}
10063
10183
  }),
10064
10184
  checkoutField(t("checkout.contact.name"), TID.checkoutName, form.name, set("name"), {
10065
10185
  placeholder: t("checkout.contact.name")
@@ -14627,5 +14747,5 @@ ${formatErrors(report.errors)}`;
14627
14747
  }
14628
14748
 
14629
14749
  export { AddressForm, AddressSheet, AddressStrip, BEFORE_PLACE_ORDER_VALVE, BLOCK_STRUCTURAL_KEYS, BUYER_COUNTRY_COOKIE, BenefitsRow, BlockDataError, CART_LINE_ANNOTATION_POINT, CATALOG_BODY_BLOCK, CHECKOUT_FIELD_POINT, CartDrawer, CatalogPage, CategoryShowcase, CheckoutPage, ClosedStoreDialog, CollectionCta, CollectionHero, ContentPage, CountdownBand, CountrySelect, DeliverToPicker, EmailConfirmationRequiredError, EntityListBlock, FaqSection, FiltersPanel, FormBlock, HeroSection, ImageTiles, ItemSheet, Link, MESSAGES, ManifestoBlock, MediaCarousel, MenuItemRow, MenuPage, MenuToolbar, MotifStrip, MyPurchasesPage, NewsletterBand, ORDER_METADATA_POINT, OrderConfirmationPage, OrderTrackingTimeline, PAGE_DATA_SCRIPT_ID, PLACEHOLDER_ASPECTS, PRODUCT_BODY_BLOCK, PRODUCT_CARD_BADGE_POINT, PageDataScope, PaymentTerms, Price, ProductCard, ProductDetailPage, ProductEnquiryForm, ProductGallery, ProductGrid, ProductOptionSelector, ProductRail, ProductReviews, ProductSlider, ProductSpecs, ProductSpotlight, PromoBanner, QuantityInput, Reveal, STOREFRONT_COMPONENT_KEYS, STOREFRONT_PAGE_KINDS, STOREFRONT_SLOTS, STORE_BACKEND_PROVIDERS, STORE_COMMERCE_MODES, STORE_DOCUMENT_KIND, STORE_PAYMENT_METHOD_KINDS, STORE_PAYMENT_MODES, STORE_ROUTE_CHROMES, STORE_ROUTE_KINDS, STORE_SECTIONS, STORE_SECTION_NAMES, ScheduleSheet, SealsBand, SearchOverlay, ServiceHoursModal, Slot, SmoothImage, StepsSection, StoreCover, StorefrontConfigHost, StorefrontConfigProvider, StorefrontFooter, StorefrontHeader, StorefrontPage, StorefrontRouterProvider, StorefrontShell, StorefrontThemeStyle, StoryQuote, THEME_SCALE_FIELDS, THEME_SCALE_VAR_PREFIX, TID, Testimonials, TierCards, applyDeliveryPolicy, applyStoreSettings, bannerPlaceholder, blockInstanceId, buildEnquiryFromForm, checkPageBlocks, collectDataNeeds, collectOrderMetadata, contentPagePath, createStorefront, createStorefrontApp, currentStaticRenderPass, defaultPageBlocks, entityListBlockMeta, establishCustomerSession, exportStore, formatMoney, formatProductOptionSelection, getCartLineAnnotations, getCheckoutFieldContributions, getCustomerAuthAdapter, getProductCardBadges, getProductOptionGroups, hydrateStoreDocument, hydratedEntriesFor, initStorefrontRuntime, isJsonValue, isKnownPath, isNestedBlockRef, isPublicPath, isRecord, listSlots, marketForCountry, marketUnitPrice, matchContentPage, matchPath, matchesFacets, mergeCollectionFacet, mergePageTheme, navigateTo, nextMilestone, normalizeLanguage, normalizeProductOptionSelection, orderGalleryImages, overrideComponent, overrideProps, pageSeo, pageThemeSelector, pickProductCardImage, placeStorefrontOrder, prefersReducedMotion, primaryImageUrl, productCardComponentContract, productJsonLd, productOptionSelectionKey, productPlaceholder, productSeo, readHydratedPageData, registerStorefrontBlocks, registerStorefrontEntities, registerStorefrontExtensionPoints, registerStorefrontLoaders, resetPageDiagnostics, resetProductOptionsDeprecationWarnings, resetStorefrontExtensionPoints, resolveAuthAdapter, resolveConfig, resolveDataParams, resolveOverride, resolvePageData, resolvePageDataNeed, resolvePaymentTerms, resolveStorefrontProvider, resolveStorefrontRoute, resolveVariantForSelection, roundCents, routePathParams, runBeforePlaceOrderValve, sectionToJsonSchema, sectionsToBlocks, selectCount, selectDiscountTotal, selectIsOpen, selectRequiresShipping, selectShipping, selectShowsHours, selectSubtotal, selectTotal, selectionPrice, serializePageData, shellRouterAdapter, signInByEmail, signOutCustomer, signUpCustomer, slugifyStoreName, storefrontComponentContracts, storefrontPaths, themeToCss, toBlockNodes, toFacetMap, translate, useBlockData, useBuyerCountry, useBuyerStore, useCartStore, useCatalogStore, useCategories, useDeliveryStore, useDiscountValidator, useEnquiry, useHashPath, useInView, useLoader, useMarketProduct, useMarketStore, useMyOrders, useNavigate, usePopOnChange, useProduct, useProducts, useRoutePath, useRouterAdapter, useScrollToTopOnNavigate, useScrolled, useServiceStore, useSessionStore, useSlotContext, useStorefrontActions, useStorefrontConfig, useStorefrontConfigOptional, useStorefrontHead, useStorefrontPage, useStorefrontPageOptional, useT, usedSlots, validateStore, withFacet, withMarketPrice, withStaticRenderPass };
14630
- //# sourceMappingURL=chunk-WFJH7SUO.js.map
14631
- //# sourceMappingURL=chunk-WFJH7SUO.js.map
14750
+ //# sourceMappingURL=chunk-OK5U3JNM.js.map
14751
+ //# sourceMappingURL=chunk-OK5U3JNM.js.map