@shoppexio/storefront-react 0.1.1 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ - Cart: every add carries the add-ons the merchant marked required
6
+ (`withRequiredAddons`), including adds from cards, bundle dialogs and
7
+ bundle offers, so the quote and the invoice include the mandatory
8
+ surcharge.
9
+
10
+ ## 0.1.2
11
+
12
+ - Contact form: a message that is longer than the 2,000-character ticket
13
+ limit once the name line is added is refused with an error naming the
14
+ limit, instead of being cut off and reported as sent.
15
+ - Contact form: a subject over 30 characters is refused, not cut.
16
+ - `loadLatestWrittenReviews(limit, labels)`: the newest written reviews for a
17
+ sample, paging the feed only until it has enough. `getShopReviewsPage` is
18
+ typed on the SDK global.
19
+
3
20
  ## 0.1.1
4
21
 
5
22
  - README rewritten for theme developers: install, quick start, theming,
package/dist/index.d.ts CHANGED
@@ -892,6 +892,20 @@ interface ShoppexSdk {
892
892
  success: boolean;
893
893
  data?: SdkShopReview[];
894
894
  }>;
895
+ /**
896
+ * One page of the same feed, newest first. `cursor` is the previous page's
897
+ * `pagination.next_cursor`; omit it for the first page.
898
+ */
899
+ getShopReviewsPage(cursor?: string | null): Promise<{
900
+ success: boolean;
901
+ data?: {
902
+ feedback: SdkShopReview[];
903
+ pagination: {
904
+ next_cursor: string | null;
905
+ has_more: boolean;
906
+ };
907
+ };
908
+ }>;
895
909
  /** Starts an in-page sign-in. Sends the email and nothing else. */
896
910
  requestOtp(email: string): Promise<SdkResponse<unknown>>;
897
911
  /**
@@ -2264,6 +2278,15 @@ declare function useCart(): CartContextValue;
2264
2278
  * the call site rather than leaving the rejection unhandled — the error is
2265
2279
  * displayed, not swallowed.
2266
2280
  */
2281
+ /**
2282
+ * The add-ons a cart line carries: the caller's choice plus every add-on the
2283
+ * merchant marked required, once each.
2284
+ *
2285
+ * PAYMENT PATH. The buy box preselects and locks required add-ons, but a card,
2286
+ * a bundle dialog or a related tile adds without choosing any. Without this the
2287
+ * line, its quote and the invoice omit a surcharge the merchant made mandatory.
2288
+ */
2289
+ declare function withRequiredAddons(product: StorefrontProduct, chosen: readonly ProductAddon[]): ProductAddon[];
2267
2290
  declare function startCartMutation(mutation: Promise<void>): void;
2268
2291
 
2269
2292
  /**
@@ -2894,6 +2917,14 @@ declare function toShopReviews(rows: readonly SdkShopReview[], labels: ShopRevie
2894
2917
  * derive a number from them.
2895
2918
  */
2896
2919
  declare function reviewsForProduct(reviews: readonly ShopReview[], productTitle: string): ShopReview[];
2920
+ /**
2921
+ * The newest WRITTEN reviews, at most `limit`, for a sample such as a home
2922
+ * section. Pages through the feed only until it has enough: a shop with a
2923
+ * thousand reviews costs one request, not ten. Automated entries and ratings
2924
+ * without words are skipped, since a sample of quotes needs quotes.
2925
+ * Resolves null when a page request fails.
2926
+ */
2927
+ declare function loadLatestWrittenReviews(limit: number, labels: ShopReviewLabels): Promise<ShopReview[] | null>;
2897
2928
  declare function averageRating(reviews: readonly ShopReview[]): number | null;
2898
2929
  declare function useShopReviews(labels: ShopReviewLabels): ShopReviewsState;
2899
2930
 
@@ -3084,4 +3115,4 @@ declare function useOnlineUserCount(): number | null;
3084
3115
  */
3085
3116
  declare function resolveProductVideoEmbedSrc(value: unknown): string | null;
3086
3117
 
3087
- export { ACCOUNT_ARTIFACT_CAPABILITIES, ACCOUNT_PATH, ACCOUNT_PATH_ALIAS, ACCOUNT_ROUTE_MESSAGE_KEYS, ACCOUNT_STOREFRONT_ROUTES, ACCOUNT_TABS, type AccountRouteMessageKey, type AccountRouteParts, type AccountTab, type AccountView, type BuyBoxVariantModel, CATALOG_PAGE_SIZE, CATALOG_SORTS, CATALOG_SORT_LABELS, type CartBoundsHeal, CartProvider, type CartQuoteLineBounds, type CartTotals, type CartView, type CartViewLine, type CatalogCardModel, type CatalogPage, type CatalogSort, type CheckoutFieldGroup, type CheckoutFieldValues, type CheckoutFields, type ContactFormValues, type ContactSubmissionContext, DEFAULT_ACCOUNT_TAB, DEFAULT_LOW_STOCK_THRESHOLD, DELIVERY_TIME_UNITS, type DeliveryPromise, type DeliveryTimeUnit, type GroupCardModel, IMPLEMENTED_ACCOUNT_TABS, type ImplementedAccountTab, type LicenseStatement, type LicenseTerm, type LinePricing, MAX_PAY_WHAT_YOU_WANT_PRICE, type MenuNavLink, type MenuSlot, type NavLink, type NavigationLinks, PRODUCT_BADGE_ICON_NAMES, type ProductAddon, type ProductAutomaticDeal, type ProductBadge, type ProductBadgeIconName, type ProductBundle, type ProductBundleItem, type ProductCardModel, type ProductCategoryRef, type ProductContentTab, type ProductCustomField, type ProductDetailModel, type ProductFaq, type ProductRatingModel, type ProductSubscription, type ProductType, QUOTE_UNAVAILABLE_CHECKOUT_ERROR, type RatingStars, type ResellerProgramSummary, type RewardProgramItem, type RewardProgramSummary, SERVING_CONTEXT_HINT, SHOP_REVIEW_PAGE_SIZE, STOREFRONT_SORT_KEYS, type SdkCartAddOptions, type SdkCartAddon, type SdkCartAppliedDiscount, type SdkCartCodeSource, type SdkCartItem, type SdkCartQuote, type SdkCartQuoteLine, type SdkCheckoutResult, type SdkCustomerAffiliateLink, type SdkCustomerAffiliateSummary, type SdkCustomerDashboard, type SdkCustomerDashboardOrder, type SdkCustomerDeliverable, type SdkCustomerDeliveryStatus, type SdkCustomerDeliverySummary, type SdkCustomerLicense, type SdkCustomerLoyalty, type SdkCustomerLoyaltyRedeem, type SdkCustomerLoyaltyTransactionType, type SdkCustomerOrderDetail, type SdkCustomerOrderPage, type SdkCustomerOrderSummary, type SdkCustomerRewardActivity, type SdkCustomerRewards, type SdkCustomerSubscription, type SdkCustomerTicketSummary, type SdkCustomerWarranty, type SdkCustomerWarrantyClaim, type SdkCustomerWarrantyClaimStatus, type SdkCustomerWarrantyList, type SdkCustomerWarrantyStatus, type SdkEligibleCartDeal, type SdkInitOptions, type SdkMenu, type SdkMenuItem, type SdkPage, type SdkRecentSaleEntry, type SdkResponse, type SdkShopReview, type ShopPageFaqItem, type ShopPageModel, type ShopPageState, type ShopReview, type ShopReviewLabels, type ShopReviewsState, type ShoppexSdk, type SocialLink, type SocialNetwork, type StockChip, type StockTone, type StorefrontAddon, type StorefrontCatalog, type StorefrontCategory, type StorefrontData, type StorefrontGroup, type StorefrontListingItem, type StorefrontPriceVariant, type StorefrontProduct, StorefrontProvider, type StorefrontSettings, type StorefrontShop, type StorefrontShopStats, type StorefrontSortKey, type SubscriptionIntervalUnit, type SupportChannel, type VolumeDiscountRule, accountOrderHref, accountRouteMessage, accountTabHref, accountTabLabelKey, averageRating, badgesForCard, badgesForProductPage, buildCartTotals, buildCartView, buildCustomFieldPayload, buildInitialCheckoutFieldValues, buildProductDetail, buildStorefrontCatalog, calculateDisplayLineTotal, calculateLinePricing, canonicalAccountPath, checkoutFieldKey, checkoutFieldWriteRepriced, checkoutNeedsDetails, clampQuantity, clearRecoveredQuoteCheckoutError, filterCatalogCards, findStorefrontProduct, firstOptionalString, flattenMerchantHtmlToText, formatCompactCount, formatDeliveryTime, formatLicenseTerm, formatMoney, formatSubscriptionCadence, formatSubscriptionTrial, getAuthoredContent, getBuyerCurrency, getProductGroups, getProducts, getShoppex, getStorefrontAddons, getStorefrontCategories, getStorefrontListing, getStorefrontSettings, getStorefrontShop, hasInjectedStorefrontData, isAccountTab, isBlankMerchantHtml, isCheckboxValueChecked, isImplementedAccountTab, isProductSoldOut, isRecord, isSelectionSoldOut, loadShopPage, missingRequiredCheckoutValues, optionalBoolean, optionalString, optionalStringWithDefault, paginateCatalogCards, parseAccountPath, parseCartQuoteLines, parseEnum, parseFiniteNumber, parseProductBadges, parseStorefrontSortKey, readCheckoutNeedsDetails, readStorefrontData, requireArray, requireBoolean, requireNumber, requireRecord, requireString, requireStringFromKeys, requireUnknownRecord, resolveAccountView, resolveBadgeForeground, resolveCartBoundsHeals, resolveCategorySelection, resolveCheckoutFields, resolveDeliveryPromise, resolveDisplayStock, resolveHideStockCounter, resolveLicenseTerm, resolveMenuLinksForSlot, resolvePayWhatYouWantMinimum, resolveProductVideoEmbedSrc, resolveQuantityBounds, resolveSupportChannels, resolveUnsellableLineIds, resolveVariantStock, reviewsForProduct, roundCurrency, sanitizeMerchantHtml, sortCatalogCards, startCartMutation, stripMerchantHtmlImages, submitContactTicket, toShopReviews, toStarCount, useCart, useNavigationLinks, useOnlineUserCount, useShopReviews, useStorefront, validateCheckoutFieldValue, validateCheckoutFields, validateContactSubmission };
3118
+ export { ACCOUNT_ARTIFACT_CAPABILITIES, ACCOUNT_PATH, ACCOUNT_PATH_ALIAS, ACCOUNT_ROUTE_MESSAGE_KEYS, ACCOUNT_STOREFRONT_ROUTES, ACCOUNT_TABS, type AccountRouteMessageKey, type AccountRouteParts, type AccountTab, type AccountView, type BuyBoxVariantModel, CATALOG_PAGE_SIZE, CATALOG_SORTS, CATALOG_SORT_LABELS, type CartBoundsHeal, CartProvider, type CartQuoteLineBounds, type CartTotals, type CartView, type CartViewLine, type CatalogCardModel, type CatalogPage, type CatalogSort, type CheckoutFieldGroup, type CheckoutFieldValues, type CheckoutFields, type ContactFormValues, type ContactSubmissionContext, DEFAULT_ACCOUNT_TAB, DEFAULT_LOW_STOCK_THRESHOLD, DELIVERY_TIME_UNITS, type DeliveryPromise, type DeliveryTimeUnit, type GroupCardModel, IMPLEMENTED_ACCOUNT_TABS, type ImplementedAccountTab, type LicenseStatement, type LicenseTerm, type LinePricing, MAX_PAY_WHAT_YOU_WANT_PRICE, type MenuNavLink, type MenuSlot, type NavLink, type NavigationLinks, PRODUCT_BADGE_ICON_NAMES, type ProductAddon, type ProductAutomaticDeal, type ProductBadge, type ProductBadgeIconName, type ProductBundle, type ProductBundleItem, type ProductCardModel, type ProductCategoryRef, type ProductContentTab, type ProductCustomField, type ProductDetailModel, type ProductFaq, type ProductRatingModel, type ProductSubscription, type ProductType, QUOTE_UNAVAILABLE_CHECKOUT_ERROR, type RatingStars, type ResellerProgramSummary, type RewardProgramItem, type RewardProgramSummary, SERVING_CONTEXT_HINT, SHOP_REVIEW_PAGE_SIZE, STOREFRONT_SORT_KEYS, type SdkCartAddOptions, type SdkCartAddon, type SdkCartAppliedDiscount, type SdkCartCodeSource, type SdkCartItem, type SdkCartQuote, type SdkCartQuoteLine, type SdkCheckoutResult, type SdkCustomerAffiliateLink, type SdkCustomerAffiliateSummary, type SdkCustomerDashboard, type SdkCustomerDashboardOrder, type SdkCustomerDeliverable, type SdkCustomerDeliveryStatus, type SdkCustomerDeliverySummary, type SdkCustomerLicense, type SdkCustomerLoyalty, type SdkCustomerLoyaltyRedeem, type SdkCustomerLoyaltyTransactionType, type SdkCustomerOrderDetail, type SdkCustomerOrderPage, type SdkCustomerOrderSummary, type SdkCustomerRewardActivity, type SdkCustomerRewards, type SdkCustomerSubscription, type SdkCustomerTicketSummary, type SdkCustomerWarranty, type SdkCustomerWarrantyClaim, type SdkCustomerWarrantyClaimStatus, type SdkCustomerWarrantyList, type SdkCustomerWarrantyStatus, type SdkEligibleCartDeal, type SdkInitOptions, type SdkMenu, type SdkMenuItem, type SdkPage, type SdkRecentSaleEntry, type SdkResponse, type SdkShopReview, type ShopPageFaqItem, type ShopPageModel, type ShopPageState, type ShopReview, type ShopReviewLabels, type ShopReviewsState, type ShoppexSdk, type SocialLink, type SocialNetwork, type StockChip, type StockTone, type StorefrontAddon, type StorefrontCatalog, type StorefrontCategory, type StorefrontData, type StorefrontGroup, type StorefrontListingItem, type StorefrontPriceVariant, type StorefrontProduct, StorefrontProvider, type StorefrontSettings, type StorefrontShop, type StorefrontShopStats, type StorefrontSortKey, type SubscriptionIntervalUnit, type SupportChannel, type VolumeDiscountRule, accountOrderHref, accountRouteMessage, accountTabHref, accountTabLabelKey, averageRating, badgesForCard, badgesForProductPage, buildCartTotals, buildCartView, buildCustomFieldPayload, buildInitialCheckoutFieldValues, buildProductDetail, buildStorefrontCatalog, calculateDisplayLineTotal, calculateLinePricing, canonicalAccountPath, checkoutFieldKey, checkoutFieldWriteRepriced, checkoutNeedsDetails, clampQuantity, clearRecoveredQuoteCheckoutError, filterCatalogCards, findStorefrontProduct, firstOptionalString, flattenMerchantHtmlToText, formatCompactCount, formatDeliveryTime, formatLicenseTerm, formatMoney, formatSubscriptionCadence, formatSubscriptionTrial, getAuthoredContent, getBuyerCurrency, getProductGroups, getProducts, getShoppex, getStorefrontAddons, getStorefrontCategories, getStorefrontListing, getStorefrontSettings, getStorefrontShop, hasInjectedStorefrontData, isAccountTab, isBlankMerchantHtml, isCheckboxValueChecked, isImplementedAccountTab, isProductSoldOut, isRecord, isSelectionSoldOut, loadLatestWrittenReviews, loadShopPage, missingRequiredCheckoutValues, optionalBoolean, optionalString, optionalStringWithDefault, paginateCatalogCards, parseAccountPath, parseCartQuoteLines, parseEnum, parseFiniteNumber, parseProductBadges, parseStorefrontSortKey, readCheckoutNeedsDetails, readStorefrontData, requireArray, requireBoolean, requireNumber, requireRecord, requireString, requireStringFromKeys, requireUnknownRecord, resolveAccountView, resolveBadgeForeground, resolveCartBoundsHeals, resolveCategorySelection, resolveCheckoutFields, resolveDeliveryPromise, resolveDisplayStock, resolveHideStockCounter, resolveLicenseTerm, resolveMenuLinksForSlot, resolvePayWhatYouWantMinimum, resolveProductVideoEmbedSrc, resolveQuantityBounds, resolveSupportChannels, resolveUnsellableLineIds, resolveVariantStock, reviewsForProduct, roundCurrency, sanitizeMerchantHtml, sortCatalogCards, startCartMutation, stripMerchantHtmlImages, submitContactTicket, toShopReviews, toStarCount, useCart, useNavigationLinks, useOnlineUserCount, useShopReviews, useStorefront, validateCheckoutFieldValue, validateCheckoutFields, validateContactSubmission, withRequiredAddons };
package/dist/index.js CHANGED
@@ -1998,7 +1998,7 @@ function CartProvider({ children }) {
1998
1998
  );
1999
1999
  }
2000
2000
  const { min: minimum, max: maximum } = resolveQuantityBounds(product, variant);
2001
- const addons = options?.addons ?? [];
2001
+ const addons = withRequiredAddons(product, options?.addons ?? []);
2002
2002
  const payWhatYouWantPrice = options?.payWhatYouWantPrice ?? (product.payWhatYouWant ? resolvePayWhatYouWantMinimum(product, variant) : void 0);
2003
2003
  const addonsUnitPrice = addons.reduce((sum, addon) => sum + addon.price, 0);
2004
2004
  const unitPriceSnapshot = payWhatYouWantPrice !== void 0 ? payWhatYouWantPrice + addonsUnitPrice : calculateLinePricing(product, variant, addons, quantity).effectiveUnitPrice;
@@ -2039,12 +2039,18 @@ function CartProvider({ children }) {
2039
2039
  }
2040
2040
  const { bounds } = member;
2041
2041
  const payWhatYouWantPrice = member.product.payWhatYouWant ? resolvePayWhatYouWantMinimum(member.product, member.variant) : void 0;
2042
+ const requiredAddons = withRequiredAddons(member.product, []);
2043
+ const addonsUnitPrice = requiredAddons.reduce((sum, addon) => sum + addon.price, 0);
2042
2044
  const addOptions = {
2043
2045
  ...item.variantId !== "default" ? { price_variant_id: item.variantId } : {},
2044
2046
  // The offer states each member's unit price; a line without that
2045
2047
  // snapshot shows no money in the cart until the quote answers. A named
2046
2048
  // price overrides it, because that is the amount the backend charges.
2047
- ...payWhatYouWantPrice !== void 0 ? { price_data: { unit_price: payWhatYouWantPrice }, pay_what_you_want_price: payWhatYouWantPrice } : item.unitPrice > 0 ? { price_data: { unit_price: item.unitPrice } } : {},
2049
+ ...payWhatYouWantPrice !== void 0 ? { price_data: { unit_price: payWhatYouWantPrice + addonsUnitPrice }, pay_what_you_want_price: payWhatYouWantPrice } : item.unitPrice > 0 ? { price_data: { unit_price: item.unitPrice + addonsUnitPrice } } : {},
2050
+ ...requiredAddons.length > 0 ? {
2051
+ addons: requiredAddons.map((addon) => ({ id: addon.id })),
2052
+ addon_labels: requiredAddons.map((addon) => addon.title)
2053
+ } : {},
2048
2054
  title: item.title,
2049
2055
  ...item.variantTitle ? { variant_title: item.variantTitle } : {},
2050
2056
  ...item.imageUrl ? { image_url: item.imageUrl } : {},
@@ -2327,6 +2333,10 @@ function useCart() {
2327
2333
  if (!context) throw new Error("useCart must be used inside CartProvider.");
2328
2334
  return context;
2329
2335
  }
2336
+ function withRequiredAddons(product, chosen) {
2337
+ const ids = new Set(chosen.map((addon) => addon.id));
2338
+ return [...chosen, ...product.addons.filter((addon) => addon.required && !ids.has(addon.id))];
2339
+ }
2330
2340
  function startCartMutation(mutation) {
2331
2341
  void mutation.catch(() => void 0);
2332
2342
  }
@@ -2416,6 +2426,12 @@ function validateContactSubmission(values, context) {
2416
2426
  if (context.requireInvoice && !values.invoiceId.trim()) {
2417
2427
  return "An order or invoice ID is required.";
2418
2428
  }
2429
+ if (values.subject.trim().length > MAX_TITLE_LENGTH) {
2430
+ return `Keep the subject under ${MAX_TITLE_LENGTH} characters.`;
2431
+ }
2432
+ if (buildTicketMessage(values).length > MESSAGE_MAX_LENGTH) {
2433
+ return `Your message is too long. Keep it under ${MESSAGE_MAX_LENGTH.toLocaleString("en-US")} characters.`;
2434
+ }
2419
2435
  return null;
2420
2436
  }
2421
2437
  function requiredGlobal(name) {
@@ -2432,7 +2448,7 @@ function buildTicketMessage(values) {
2432
2448
  const body = values.message.trim();
2433
2449
  return `${name ? `Name: ${name}
2434
2450
 
2435
- ` : ""}${body}`.slice(0, MESSAGE_MAX_LENGTH);
2451
+ ` : ""}${body}`;
2436
2452
  }
2437
2453
  async function submitContactTicket(values, context) {
2438
2454
  const error = validateContactSubmission(values, context);
@@ -2448,7 +2464,7 @@ async function submitContactTicket(values, context) {
2448
2464
  headers: { "Content-Type": "application/json", Accept: "application/json" },
2449
2465
  body: JSON.stringify({
2450
2466
  email: values.email.trim().toLowerCase(),
2451
- title: subject.length >= MIN_SUBJECT_LENGTH ? subject.slice(0, MAX_TITLE_LENGTH) : DEFAULT_TICKET_TITLE,
2467
+ title: subject.length >= MIN_SUBJECT_LENGTH ? subject : DEFAULT_TICKET_TITLE,
2452
2468
  message: buildTicketMessage(values),
2453
2469
  ...invoiceId ? { invoice_id: invoiceId } : {}
2454
2470
  })
@@ -2571,6 +2587,21 @@ function reviewsForProduct(reviews, productTitle) {
2571
2587
  if (!title) return [];
2572
2588
  return reviews.filter((review) => review.productTitle === title);
2573
2589
  }
2590
+ async function loadLatestWrittenReviews(limit, labels) {
2591
+ const written = [];
2592
+ const seen = /* @__PURE__ */ new Set();
2593
+ let cursor = null;
2594
+ while (written.length < limit) {
2595
+ const result = await getShoppex().getShopReviewsPage(cursor);
2596
+ if (!result.success || !result.data) return null;
2597
+ written.push(...result.data.feedback.filter((row) => !row.is_automated && (row.comment ?? row.message ?? "").trim() !== ""));
2598
+ const next = result.data.pagination.has_more ? result.data.pagination.next_cursor : null;
2599
+ if (!next || seen.has(next)) break;
2600
+ seen.add(next);
2601
+ cursor = next;
2602
+ }
2603
+ return toShopReviews(written, labels).slice(0, limit);
2604
+ }
2574
2605
  function averageRating(reviews) {
2575
2606
  if (reviews.length === 0) return null;
2576
2607
  return reviews.reduce((total, review) => total + review.rating, 0) / reviews.length;
@@ -2743,6 +2774,7 @@ export {
2743
2774
  isProductSoldOut,
2744
2775
  isRecord,
2745
2776
  isSelectionSoldOut,
2777
+ loadLatestWrittenReviews,
2746
2778
  loadShopPage,
2747
2779
  missingRequiredCheckoutValues,
2748
2780
  optionalBoolean,
@@ -2796,6 +2828,7 @@ export {
2796
2828
  useStorefront,
2797
2829
  validateCheckoutFieldValue,
2798
2830
  validateCheckoutFields,
2799
- validateContactSubmission
2831
+ validateContactSubmission,
2832
+ withRequiredAddons
2800
2833
  };
2801
2834
  //# sourceMappingURL=index.js.map