@pradip1995/create-storefront 1.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/bin/create-storefront.js +239 -0
- package/lib/kit-next-config.js +84 -0
- package/package.json +32 -0
- package/templates/storefront/.eslintrc.json +3 -0
- package/templates/storefront/README.md +35 -0
- package/templates/storefront/check-env-variables.js +51 -0
- package/templates/storefront/kit-next-config.js +71 -0
- package/templates/storefront/next-env.d.ts +5 -0
- package/templates/storefront/next.config.js +25 -0
- package/templates/storefront/package.json +56 -0
- package/templates/storefront/postcss.config.js +6 -0
- package/templates/storefront/public/favicon.png +0 -0
- package/templates/storefront/src/app/[countryCode]/(checkout)/checkout/page.tsx +23 -0
- package/templates/storefront/src/app/[countryCode]/(checkout)/checkout/payment/page.tsx +47 -0
- package/templates/storefront/src/app/[countryCode]/(checkout)/layout.tsx +31 -0
- package/templates/storefront/src/app/[countryCode]/(checkout)/not-found.tsx +19 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/addresses/page.tsx +31 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/loading.tsx +9 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/details/[id]/page.tsx +35 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/exchange/[id]/page.tsx +47 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/page.tsx +28 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/return/[id]/page.tsx +66 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/page.tsx +22 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/payment-methods/page.tsx +23 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/profile/page.tsx +43 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@login/default.tsx +11 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/@login/page.tsx +18 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/guest-orders/page.tsx +13 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/layout.tsx +22 -0
- package/templates/storefront/src/app/[countryCode]/(main)/account/loading.tsx +9 -0
- package/templates/storefront/src/app/[countryCode]/(main)/cart/loading.tsx +5 -0
- package/templates/storefront/src/app/[countryCode]/(main)/cart/not-found.tsx +21 -0
- package/templates/storefront/src/app/[countryCode]/(main)/cart/page.tsx +23 -0
- package/templates/storefront/src/app/[countryCode]/(main)/categories/[...category]/page.tsx +11 -0
- package/templates/storefront/src/app/[countryCode]/(main)/collections/[handle]/page.tsx +11 -0
- package/templates/storefront/src/app/[countryCode]/(main)/contact/page.tsx +21 -0
- package/templates/storefront/src/app/[countryCode]/(main)/guest-orders/page.tsx +12 -0
- package/templates/storefront/src/app/[countryCode]/(main)/help/page.tsx +28 -0
- package/templates/storefront/src/app/[countryCode]/(main)/layout.tsx +21 -0
- package/templates/storefront/src/app/[countryCode]/(main)/not-found.tsx +20 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/confirmed/loading.tsx +5 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/confirmed/page.tsx +23 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/accept/page.tsx +41 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/decline/page.tsx +41 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/page.tsx +38 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/exchange/[id]/page.tsx +47 -0
- package/templates/storefront/src/app/[countryCode]/(main)/order/return/[id]/page.tsx +61 -0
- package/templates/storefront/src/app/[countryCode]/(main)/orders/[id]/page.tsx +33 -0
- package/templates/storefront/src/app/[countryCode]/(main)/page.tsx +24 -0
- package/templates/storefront/src/app/[countryCode]/(main)/privacy-policy/page.tsx +173 -0
- package/templates/storefront/src/app/[countryCode]/(main)/products/[handle]/page.tsx +193 -0
- package/templates/storefront/src/app/[countryCode]/(main)/reset-password/page.tsx +192 -0
- package/templates/storefront/src/app/[countryCode]/(main)/store/page.tsx +72 -0
- package/templates/storefront/src/app/[countryCode]/(main)/terms-of-use/page.tsx +179 -0
- package/templates/storefront/src/app/[countryCode]/(main)/wishlist/page.tsx +19 -0
- package/templates/storefront/src/app/api/meta/event/route.ts +63 -0
- package/templates/storefront/src/app/auth/customer/google/callback/page.tsx +126 -0
- package/templates/storefront/src/app/layout.tsx +104 -0
- package/templates/storefront/src/app/not-found.tsx +30 -0
- package/templates/storefront/src/app/opengraph-image.jpg +0 -0
- package/templates/storefront/src/app/robots.ts +15 -0
- package/templates/storefront/src/app/sitemap.ts +65 -0
- package/templates/storefront/src/app/twitter-image.jpg +0 -0
- package/templates/storefront/src/modules/account/components/account-deletion/index.tsx +160 -0
- package/templates/storefront/src/modules/account/components/account-info/index.tsx +145 -0
- package/templates/storefront/src/modules/account/components/account-nav/icons.tsx +43 -0
- package/templates/storefront/src/modules/account/components/account-nav/index.tsx +318 -0
- package/templates/storefront/src/modules/account/components/account-nav/logout-modal.tsx +92 -0
- package/templates/storefront/src/modules/account/components/account-nav/payment-methods-icon.tsx +9 -0
- package/templates/storefront/src/modules/account/components/address-book/index.tsx +47 -0
- package/templates/storefront/src/modules/account/components/address-card/add-address.tsx +377 -0
- package/templates/storefront/src/modules/account/components/address-card/edit-address-modal.tsx +468 -0
- package/templates/storefront/src/modules/account/components/deletion-pending-modal/index.tsx +213 -0
- package/templates/storefront/src/modules/account/components/forgot-password/index.tsx +1 -0
- package/templates/storefront/src/modules/account/components/login/index.tsx +1 -0
- package/templates/storefront/src/modules/account/components/order-card/index.tsx +221 -0
- package/templates/storefront/src/modules/account/components/order-overview/index.tsx +159 -0
- package/templates/storefront/src/modules/account/components/overview/index.tsx +189 -0
- package/templates/storefront/src/modules/account/components/profile-billing-address/index.tsx +447 -0
- package/templates/storefront/src/modules/account/components/profile-email/index.tsx +75 -0
- package/templates/storefront/src/modules/account/components/profile-form/index.tsx +416 -0
- package/templates/storefront/src/modules/account/components/profile-name/index.tsx +76 -0
- package/templates/storefront/src/modules/account/components/profile-password/index.tsx +70 -0
- package/templates/storefront/src/modules/account/components/profile-phone/index.tsx +185 -0
- package/templates/storefront/src/modules/account/components/register/index.tsx +1 -0
- package/templates/storefront/src/modules/account/components/return-item-selector/index.tsx +187 -0
- package/templates/storefront/src/modules/account/components/return-shipping-selector/index.tsx +118 -0
- package/templates/storefront/src/modules/account/components/transfer-request-form/index.tsx +81 -0
- package/templates/storefront/src/modules/account/templates/account-layout.tsx +38 -0
- package/templates/storefront/src/modules/account/templates/exchange-request-template.tsx +389 -0
- package/templates/storefront/src/modules/account/templates/guest-orders-template.tsx +123 -0
- package/templates/storefront/src/modules/account/templates/login-template.tsx +44 -0
- package/templates/storefront/src/modules/account/templates/payment-methods-template.tsx +478 -0
- package/templates/storefront/src/modules/account/templates/return-request-template.tsx +300 -0
- package/templates/storefront/src/modules/cart/components/abandoned-carts/ScrollToPendingOrdersButton.tsx +21 -0
- package/templates/storefront/src/modules/cart/components/abandoned-carts/index.tsx +335 -0
- package/templates/storefront/src/modules/cart/components/applied-promotions/index.tsx +121 -0
- package/templates/storefront/src/modules/cart/components/cart-delivery-selection/index.tsx +203 -0
- package/templates/storefront/src/modules/cart/components/cart-item-card/index.tsx +476 -0
- package/templates/storefront/src/modules/cart/components/cart-item-select/index.tsx +73 -0
- package/templates/storefront/src/modules/cart/components/cart-view-tracker/index.tsx +44 -0
- package/templates/storefront/src/modules/cart/components/delivery-information/index.tsx +89 -0
- package/templates/storefront/src/modules/cart/components/empty-cart-message/index.tsx +38 -0
- package/templates/storefront/src/modules/cart/components/item/index.tsx +150 -0
- package/templates/storefront/src/modules/cart/components/pincode-checker/index.tsx +174 -0
- package/templates/storefront/src/modules/cart/components/sign-in-prompt/index.tsx +26 -0
- package/templates/storefront/src/modules/cart/components/you-may-also-like/index.tsx +137 -0
- package/templates/storefront/src/modules/cart/templates/index.tsx +88 -0
- package/templates/storefront/src/modules/cart/templates/items.tsx +49 -0
- package/templates/storefront/src/modules/cart/templates/preview.tsx +51 -0
- package/templates/storefront/src/modules/cart/templates/summary.tsx +29 -0
- package/templates/storefront/src/modules/checkout/components/add-address-modal/index.tsx +390 -0
- package/templates/storefront/src/modules/checkout/components/address-card/index.tsx +135 -0
- package/templates/storefront/src/modules/checkout/components/address-select/index.tsx +116 -0
- package/templates/storefront/src/modules/checkout/components/addresses/index.tsx +605 -0
- package/templates/storefront/src/modules/checkout/components/back-link/index.tsx +32 -0
- package/templates/storefront/src/modules/checkout/components/billing_address/index.tsx +301 -0
- package/templates/storefront/src/modules/checkout/components/checkout-begin-tracker/index.tsx +45 -0
- package/templates/storefront/src/modules/checkout/components/checkout-leave-guard/index.tsx +109 -0
- package/templates/storefront/src/modules/checkout/components/checkout-shipping-tracker/index.tsx +45 -0
- package/templates/storefront/src/modules/checkout/components/country-select/index.tsx +50 -0
- package/templates/storefront/src/modules/checkout/components/discount-code/index.tsx +220 -0
- package/templates/storefront/src/modules/checkout/components/error-message/index.tsx +13 -0
- package/templates/storefront/src/modules/checkout/components/payment/index.tsx +572 -0
- package/templates/storefront/src/modules/checkout/components/payment-button/index.tsx +257 -0
- package/templates/storefront/src/modules/checkout/components/payment-button/razorpay-payment-button.tsx +136 -0
- package/templates/storefront/src/modules/checkout/components/payment-container/index.tsx +129 -0
- package/templates/storefront/src/modules/checkout/components/payment-test/index.tsx +12 -0
- package/templates/storefront/src/modules/checkout/components/payment-wrapper/index.tsx +50 -0
- package/templates/storefront/src/modules/checkout/components/payment-wrapper/stripe-wrapper.tsx +54 -0
- package/templates/storefront/src/modules/checkout/components/processing-overlay/index.tsx +83 -0
- package/templates/storefront/src/modules/checkout/components/review/index.tsx +60 -0
- package/templates/storefront/src/modules/checkout/components/select-address-modal/index.tsx +103 -0
- package/templates/storefront/src/modules/checkout/components/shipping/index.tsx +533 -0
- package/templates/storefront/src/modules/checkout/components/shipping-address/index.tsx +521 -0
- package/templates/storefront/src/modules/checkout/components/submit-button/index.tsx +32 -0
- package/templates/storefront/src/modules/checkout/templates/checkout-form/index.tsx +38 -0
- package/templates/storefront/src/modules/checkout/templates/checkout-summary/index.tsx +274 -0
- package/templates/storefront/src/modules/common/components/breadcrumb/index.tsx +43 -0
- package/templates/storefront/src/modules/common/components/cart-totals/index.tsx +473 -0
- package/templates/storefront/src/modules/common/components/checkbox/index.tsx +98 -0
- package/templates/storefront/src/modules/common/components/delete-button/index.tsx +156 -0
- package/templates/storefront/src/modules/common/components/divider/index.tsx +9 -0
- package/templates/storefront/src/modules/common/components/filter-checkbox-group/index.tsx +134 -0
- package/templates/storefront/src/modules/common/components/filter-radio-group/index.tsx +62 -0
- package/templates/storefront/src/modules/common/components/input/index.tsx +79 -0
- package/templates/storefront/src/modules/common/components/interactive-link/index.tsx +33 -0
- package/templates/storefront/src/modules/common/components/line-item-options/index.tsx +26 -0
- package/templates/storefront/src/modules/common/components/line-item-price/index.tsx +64 -0
- package/templates/storefront/src/modules/common/components/line-item-unit-price/index.tsx +61 -0
- package/templates/storefront/src/modules/common/components/localized-client-link/index.tsx +32 -0
- package/templates/storefront/src/modules/common/components/login-popup/index.tsx +78 -0
- package/templates/storefront/src/modules/common/components/modal/index.tsx +123 -0
- package/templates/storefront/src/modules/common/components/native-select/index.tsx +75 -0
- package/templates/storefront/src/modules/common/components/obfuscated-email/index.tsx +30 -0
- package/templates/storefront/src/modules/common/components/product/product-rating/index.tsx +172 -0
- package/templates/storefront/src/modules/common/components/product/review-modal/index.tsx +333 -0
- package/templates/storefront/src/modules/common/components/product/share-button/index.tsx +227 -0
- package/templates/storefront/src/modules/common/components/product/wishlist-icon/index.tsx +46 -0
- package/templates/storefront/src/modules/common/components/radio/index.tsx +27 -0
- package/templates/storefront/src/modules/common/components/select/index.tsx +164 -0
- package/templates/storefront/src/modules/common/components/side-panel/index.tsx +65 -0
- package/templates/storefront/src/modules/common/icons/arrow-left.tsx +36 -0
- package/templates/storefront/src/modules/common/icons/back.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/bancontact.tsx +26 -0
- package/templates/storefront/src/modules/common/icons/chevron-down.tsx +30 -0
- package/templates/storefront/src/modules/common/icons/delivered.tsx +29 -0
- package/templates/storefront/src/modules/common/icons/envelope.tsx +27 -0
- package/templates/storefront/src/modules/common/icons/eye-off.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/eye.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/fast-delivery.tsx +65 -0
- package/templates/storefront/src/modules/common/icons/ideal.tsx +26 -0
- package/templates/storefront/src/modules/common/icons/lock.tsx +31 -0
- package/templates/storefront/src/modules/common/icons/map-pin.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/medusa.tsx +27 -0
- package/templates/storefront/src/modules/common/icons/menu.tsx +45 -0
- package/templates/storefront/src/modules/common/icons/nextjs.tsx +27 -0
- package/templates/storefront/src/modules/common/icons/package.tsx +44 -0
- package/templates/storefront/src/modules/common/icons/paypal.tsx +30 -0
- package/templates/storefront/src/modules/common/icons/phone.tsx +30 -0
- package/templates/storefront/src/modules/common/icons/placeholder-image.tsx +44 -0
- package/templates/storefront/src/modules/common/icons/refresh.tsx +51 -0
- package/templates/storefront/src/modules/common/icons/spinner.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/trash.tsx +51 -0
- package/templates/storefront/src/modules/common/icons/user.tsx +37 -0
- package/templates/storefront/src/modules/common/icons/x.tsx +37 -0
- package/templates/storefront/src/modules/contact/templates/index.tsx +272 -0
- package/templates/storefront/src/modules/help/templates/index.tsx +629 -0
- package/templates/storefront/src/modules/home/components/dynamic-banner/index.tsx +190 -0
- package/templates/storefront/src/modules/home/components/featured-products/index.tsx +16 -0
- package/templates/storefront/src/modules/home/components/featured-products/product-rail/index.tsx +51 -0
- package/templates/storefront/src/modules/home/components/features/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/hero/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/loved-by-moms/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/new-arrivals/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/shop-by-age/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/shop-by-category/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/testimonials/index.tsx +1 -0
- package/templates/storefront/src/modules/home/components/why-choose-us/dynamic-features.tsx +93 -0
- package/templates/storefront/src/modules/home/components/why-choose-us/index.tsx +1 -0
- package/templates/storefront/src/modules/layout/components/account-dropdown/index.tsx +56 -0
- package/templates/storefront/src/modules/layout/components/cart-button/index.tsx +8 -0
- package/templates/storefront/src/modules/layout/components/cart-dropdown/index.tsx +424 -0
- package/templates/storefront/src/modules/layout/components/cart-mismatch-banner/index.tsx +57 -0
- package/templates/storefront/src/modules/layout/components/cookie-consent/index.tsx +116 -0
- package/templates/storefront/src/modules/layout/components/country-select/index.tsx +135 -0
- package/templates/storefront/src/modules/layout/components/desktop-search/index.tsx +148 -0
- package/templates/storefront/src/modules/layout/components/dynamic-logo/index.tsx +27 -0
- package/templates/storefront/src/modules/layout/components/footer-categories/index.tsx +34 -0
- package/templates/storefront/src/modules/layout/components/footer-contact/index.tsx +87 -0
- package/templates/storefront/src/modules/layout/components/footer-description/index.tsx +12 -0
- package/templates/storefront/src/modules/layout/components/footer-logo/index.tsx +22 -0
- package/templates/storefront/src/modules/layout/components/footer-newsletter/index.tsx +100 -0
- package/templates/storefront/src/modules/layout/components/language-select/index.tsx +192 -0
- package/templates/storefront/src/modules/layout/components/medusa-cta/index.tsx +21 -0
- package/templates/storefront/src/modules/layout/components/mobile-menu/index.tsx +296 -0
- package/templates/storefront/src/modules/layout/components/nav-links/index.tsx +66 -0
- package/templates/storefront/src/modules/layout/components/nav-wrapper/index.tsx +14 -0
- package/templates/storefront/src/modules/layout/components/promo-bar/index.tsx +7 -0
- package/templates/storefront/src/modules/layout/components/promo-bar/promo-bar-content.tsx +174 -0
- package/templates/storefront/src/modules/layout/components/push-notification-manager/index.tsx +191 -0
- package/templates/storefront/src/modules/layout/components/search-panel/index.tsx +136 -0
- package/templates/storefront/src/modules/layout/components/side-menu/index.tsx +144 -0
- package/templates/storefront/src/modules/layout/components/verification-banner/index.tsx +217 -0
- package/templates/storefront/src/modules/layout/components/wishlist-counter/index.tsx +17 -0
- package/templates/storefront/src/modules/layout/templates/footer/index.tsx +7 -0
- package/templates/storefront/src/modules/layout/templates/nav/index.tsx +14 -0
- package/templates/storefront/src/modules/order/components/cancel-order-modal/index.tsx +168 -0
- package/templates/storefront/src/modules/order/components/help/index.tsx +25 -0
- package/templates/storefront/src/modules/order/components/item/index.tsx +62 -0
- package/templates/storefront/src/modules/order/components/items/index.tsx +44 -0
- package/templates/storefront/src/modules/order/components/onboarding-cta/index.tsx +28 -0
- package/templates/storefront/src/modules/order/components/order-confirmation-back-handler/index.tsx +28 -0
- package/templates/storefront/src/modules/order/components/order-details/index.tsx +63 -0
- package/templates/storefront/src/modules/order/components/order-purchase-tracker/index.tsx +48 -0
- package/templates/storefront/src/modules/order/components/order-redesign/index.tsx +887 -0
- package/templates/storefront/src/modules/order/components/order-summary/index.tsx +60 -0
- package/templates/storefront/src/modules/order/components/payment-details/index.tsx +63 -0
- package/templates/storefront/src/modules/order/components/shipping-details/index.tsx +73 -0
- package/templates/storefront/src/modules/order/components/transfer-actions/index.tsx +81 -0
- package/templates/storefront/src/modules/order/components/transfer-image/index.tsx +275 -0
- package/templates/storefront/src/modules/order/templates/order-completed-template.tsx +233 -0
- package/templates/storefront/src/modules/order/templates/order-details-template.tsx +128 -0
- package/templates/storefront/src/modules/products/components/image-gallery/index.tsx +297 -0
- package/templates/storefront/src/modules/products/components/product-actions/index.tsx +1400 -0
- package/templates/storefront/src/modules/products/components/product-actions/mobile-actions.tsx +217 -0
- package/templates/storefront/src/modules/products/components/product-actions/option-select.tsx +62 -0
- package/templates/storefront/src/modules/products/components/product-onboarding-cta/index.tsx +30 -0
- package/templates/storefront/src/modules/products/components/product-preview/index.tsx +5 -0
- package/templates/storefront/src/modules/products/components/product-preview/price.tsx +29 -0
- package/templates/storefront/src/modules/products/components/product-price/index.tsx +58 -0
- package/templates/storefront/src/modules/products/components/product-rating/index.tsx +1 -0
- package/templates/storefront/src/modules/products/components/product-tabs/accordion.tsx +100 -0
- package/templates/storefront/src/modules/products/components/product-tabs/index.tsx +127 -0
- package/templates/storefront/src/modules/products/components/product-tabs/ratings-tab.tsx +598 -0
- package/templates/storefront/src/modules/products/components/product-view-tracker/index.tsx +53 -0
- package/templates/storefront/src/modules/products/components/related-products/index.tsx +152 -0
- package/templates/storefront/src/modules/products/components/review-modal/index.tsx +1 -0
- package/templates/storefront/src/modules/products/components/share-button/index.tsx +1 -0
- package/templates/storefront/src/modules/products/components/thumbnail/index.tsx +91 -0
- package/templates/storefront/src/modules/products/components/wishlist-icon/index.tsx +1 -0
- package/templates/storefront/src/modules/products/context/product-context.tsx +52 -0
- package/templates/storefront/src/modules/products/templates/index.tsx +26 -0
- package/templates/storefront/src/modules/products/templates/product-actions-wrapper/index.tsx +1 -0
- package/templates/storefront/src/modules/products/templates/product-info/index.tsx +2 -0
- package/templates/storefront/src/modules/shipping/components/free-shipping-price-nudge/index.tsx +283 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-button/index.tsx +5 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-card-details/index.tsx +10 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-cart-item/index.tsx +35 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-cart-totals/index.tsx +30 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-code-form/index.tsx +13 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-line-item/index.tsx +35 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-order-confirmed-header/index.tsx +14 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-order-information/index.tsx +36 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-order-items/index.tsx +43 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-order-summary/index.tsx +15 -0
- package/templates/storefront/src/modules/skeletons/components/skeleton-product-preview/index.tsx +15 -0
- package/templates/storefront/src/modules/skeletons/templates/skeleton-cart-page/index.tsx +65 -0
- package/templates/storefront/src/modules/skeletons/templates/skeleton-order-confirmed/index.tsx +21 -0
- package/templates/storefront/src/modules/skeletons/templates/skeleton-product-grid/index.tsx +23 -0
- package/templates/storefront/src/modules/skeletons/templates/skeleton-related-products/index.tsx +25 -0
- package/templates/storefront/src/modules/store/components/client-paginated-products.tsx +108 -0
- package/templates/storefront/src/modules/store/components/mobile-filters/index.tsx +135 -0
- package/templates/storefront/src/modules/store/components/pagination/index.tsx +118 -0
- package/templates/storefront/src/modules/store/components/product-list-view-tracker/index.tsx +43 -0
- package/templates/storefront/src/modules/store/components/refinement-list/index.tsx +299 -0
- package/templates/storefront/src/modules/store/components/refinement-list/sort-products/index.tsx +120 -0
- package/templates/storefront/src/modules/store/components/store-header/index.tsx +67 -0
- package/templates/storefront/src/modules/store/templates/index.tsx +1 -0
- package/templates/storefront/src/modules/store/templates/paginated-products.tsx +175 -0
- package/templates/storefront/src/modules/wishlist/components/wishlist-item/index.tsx +797 -0
- package/templates/storefront/src/modules/wishlist/templates/index.tsx +176 -0
- package/templates/storefront/src/storefront.config.ts +12 -0
- package/templates/storefront/src/styles/globals.css +326 -0
- package/templates/storefront/src/theme/valero/blocks/home/Features/index.tsx +61 -0
- package/templates/storefront/src/theme/valero/blocks/home/Hero/index.tsx +102 -0
- package/templates/storefront/src/theme/valero/blocks/home/LovedByMoms/index.tsx +407 -0
- package/templates/storefront/src/theme/valero/blocks/home/NewArrivals/index.tsx +48 -0
- package/templates/storefront/src/theme/valero/blocks/home/ShopByAge/index.tsx +128 -0
- package/templates/storefront/src/theme/valero/blocks/home/ShopByCategory/index.tsx +409 -0
- package/templates/storefront/src/theme/valero/blocks/home/Testimonials/index.tsx +697 -0
- package/templates/storefront/src/theme/valero/blocks/home/WhyChooseUs/index.tsx +62 -0
- package/templates/storefront/src/theme/valero/layouts/MainLayoutShell.tsx +14 -0
- package/templates/storefront/src/theme/valero/primitives/Button.tsx +28 -0
- package/templates/storefront/src/theme/valero/primitives/Card.tsx +32 -0
- package/templates/storefront/src/theme/valero/primitives/index.ts +2 -0
- package/templates/storefront/src/theme/valero/slots/account/ForgotPassword/index.tsx +1 -0
- package/templates/storefront/src/theme/valero/slots/account/Login/index.tsx +1 -0
- package/templates/storefront/src/theme/valero/slots/account/LoginTemplate/index.tsx +44 -0
- package/templates/storefront/src/theme/valero/slots/account/Register/index.tsx +1 -0
- package/templates/storefront/src/theme/valero/slots/cart/CartItem/index.tsx +11 -0
- package/templates/storefront/src/theme/valero/slots/cart/CartSummary/index.tsx +13 -0
- package/templates/storefront/src/theme/valero/slots/checkout/CheckoutForm/index.tsx +1 -0
- package/templates/storefront/src/theme/valero/slots/checkout/CheckoutSummary/index.tsx +1 -0
- package/templates/storefront/src/theme/valero/slots/layout/Footer/index.tsx +104 -0
- package/templates/storefront/src/theme/valero/slots/layout/Nav/index.tsx +97 -0
- package/templates/storefront/src/theme/valero/slots/layout/PromoBar/index.tsx +19 -0
- package/templates/storefront/src/theme/valero/slots/layout/PromoBar/promo-bar-content.tsx +174 -0
- package/templates/storefront/src/theme/valero/slots/order/OrderDetails/index.tsx +12 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductCTASection.tsx +191 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductDetailsSection.tsx +137 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductFeaturePanel.tsx +245 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductHighlightsSection.tsx +98 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductOptionsSection.tsx +233 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductPriceSection.tsx +53 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductTrustSection.tsx +84 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductActions/index.tsx +161 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductCard/index.tsx +132 -0
- package/templates/storefront/src/theme/valero/slots/product/ProductInfo/index.tsx +40 -0
- package/templates/storefront/src/theme/valero/templates/StorePage/index.tsx +154 -0
- package/templates/storefront/src/theme/valero/tokens/colors.js +16 -0
- package/templates/storefront/src/theme/valero/tokens/colors.ts +21 -0
- package/templates/storefront/src/theme/valero/tokens/fonts.ts +13 -0
- package/templates/storefront/src/theme/valero/tokens/index.ts +3 -0
- package/templates/storefront/src/theme/valero/tokens/spacing.ts +9 -0
- package/templates/storefront/src/theme/valero/tokens/theme.css +91 -0
- package/templates/storefront/tailwind.config.js +221 -0
- package/templates/storefront/tsconfig.json +30 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, useMemo, useEffect } from "react"
|
|
4
|
+
import { HttpTypes } from "@medusajs/types"
|
|
5
|
+
import { updateLineItem, deleteLineItem, addToCart, retrieveCart } from "@core/data/cart"
|
|
6
|
+
import { getProductByHandle } from "@core/data/products"
|
|
7
|
+
import { trackAddToCart, trackRemoveFromCart } from "@core/analytics/ga4-ecommerce"
|
|
8
|
+
import { useParams } from "next/navigation"
|
|
9
|
+
import { isEqual } from "lodash"
|
|
10
|
+
import Thumbnail from "@modules/products/components/thumbnail"
|
|
11
|
+
import DeleteButton from "@modules/common/components/delete-button"
|
|
12
|
+
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
|
13
|
+
import Spinner from "@modules/common/icons/spinner"
|
|
14
|
+
import ErrorMessage from "@modules/checkout/components/error-message"
|
|
15
|
+
import { getProductPrice } from "@core/util/get-product-price"
|
|
16
|
+
import { convertToLocale } from "@core/util/money"
|
|
17
|
+
|
|
18
|
+
type CartItemCardProps = {
|
|
19
|
+
item: HttpTypes.StoreCartLineItem
|
|
20
|
+
currencyCode: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const optionsAsKeymap = (
|
|
24
|
+
variantOptions: HttpTypes.StoreProductVariant["options"]
|
|
25
|
+
) => {
|
|
26
|
+
return variantOptions?.reduce((acc: Record<string, string>, varopt: any) => {
|
|
27
|
+
acc[varopt.option_id] = varopt.value
|
|
28
|
+
return acc
|
|
29
|
+
}, {})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const CartItemCard = ({ item, currencyCode }: CartItemCardProps) => {
|
|
33
|
+
const [updating, setUpdating] = useState(false)
|
|
34
|
+
const [error, setError] = useState<string | null>(null)
|
|
35
|
+
const [options, setOptions] = useState<Record<string, string | undefined>>({})
|
|
36
|
+
const [freshVariant, setFreshVariant] = useState<any>(null)
|
|
37
|
+
const countryCode = useParams().countryCode as string
|
|
38
|
+
|
|
39
|
+
// Get product from cart item - it should be available since we updated cart retrieval
|
|
40
|
+
// Check both item.product and item.variant?.product
|
|
41
|
+
const product = (item as any).product || item.variant?.product
|
|
42
|
+
|
|
43
|
+
// Initialize options from current variant
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (item.variant?.options) {
|
|
46
|
+
const variantOptions = optionsAsKeymap(item.variant.options)
|
|
47
|
+
setOptions(variantOptions ?? {})
|
|
48
|
+
}
|
|
49
|
+
}, [item.variant?.options])
|
|
50
|
+
|
|
51
|
+
// Fetch fresh variant data to get accurate inventory
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const fetchFreshData = async () => {
|
|
54
|
+
if (item.product_handle) {
|
|
55
|
+
const productData = await getProductByHandle(item.product_handle)
|
|
56
|
+
if (productData?.variants) {
|
|
57
|
+
const v = productData.variants.find((v: any) => v.id === item.variant_id)
|
|
58
|
+
if (v) {
|
|
59
|
+
setFreshVariant(v)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
fetchFreshData()
|
|
65
|
+
}, [item.product_handle, item.variant_id])
|
|
66
|
+
|
|
67
|
+
// Find selected variant based on options
|
|
68
|
+
const selectedVariant = useMemo(() => {
|
|
69
|
+
if (!product?.variants || product.variants.length === 0) {
|
|
70
|
+
return item.variant
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return product.variants.find((v: HttpTypes.StoreProductVariant) => {
|
|
74
|
+
const variantOptions = optionsAsKeymap(v.options)
|
|
75
|
+
return isEqual(variantOptions, options)
|
|
76
|
+
})
|
|
77
|
+
}, [product?.variants, options, item.variant])
|
|
78
|
+
|
|
79
|
+
// Get available option values
|
|
80
|
+
const getOptionValues = (optionId: string) => {
|
|
81
|
+
if (!product?.variants) return []
|
|
82
|
+
|
|
83
|
+
const values = new Set<string>()
|
|
84
|
+
product.variants.forEach((variant: HttpTypes.StoreProductVariant) => {
|
|
85
|
+
variant.options?.forEach((opt: any) => {
|
|
86
|
+
if (opt.option_id === optionId) {
|
|
87
|
+
values.add(opt.value)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
return Array.from(values)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get product options
|
|
95
|
+
const productOptions = (product?.options || []) as HttpTypes.StoreProductOption[]
|
|
96
|
+
|
|
97
|
+
const changeQuantity = async (quantity: number) => {
|
|
98
|
+
setError(null)
|
|
99
|
+
setUpdating(true)
|
|
100
|
+
|
|
101
|
+
await updateLineItem({
|
|
102
|
+
lineId: item.id,
|
|
103
|
+
quantity,
|
|
104
|
+
})
|
|
105
|
+
.catch((err) => {
|
|
106
|
+
setError(err.message)
|
|
107
|
+
})
|
|
108
|
+
.finally(() => {
|
|
109
|
+
setUpdating(false)
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const handleOptionChange = async (optionId: string, value: string) => {
|
|
114
|
+
const newOptions = {
|
|
115
|
+
...options,
|
|
116
|
+
[optionId]: value,
|
|
117
|
+
}
|
|
118
|
+
setOptions(newOptions)
|
|
119
|
+
|
|
120
|
+
// Find variant with new options
|
|
121
|
+
const newVariant = product?.variants?.find((v: HttpTypes.StoreProductVariant) => {
|
|
122
|
+
const variantOptions = optionsAsKeymap(v.options)
|
|
123
|
+
return isEqual(variantOptions, newOptions)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (newVariant && newVariant.id !== item.variant_id) {
|
|
127
|
+
setError(null)
|
|
128
|
+
setUpdating(true)
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const unitTotal = item.total ? item.total / item.quantity : 0
|
|
132
|
+
trackRemoveFromCart({
|
|
133
|
+
currency: currencyCode,
|
|
134
|
+
value: (item.total ?? 0) / 100,
|
|
135
|
+
items: [
|
|
136
|
+
{
|
|
137
|
+
item_id: item.variant_id ?? item.id,
|
|
138
|
+
item_name: item.title ?? "",
|
|
139
|
+
price: unitTotal / 100,
|
|
140
|
+
quantity: item.quantity,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
})
|
|
144
|
+
await deleteLineItem(item.id)
|
|
145
|
+
|
|
146
|
+
await addToCart({
|
|
147
|
+
variantId: newVariant.id,
|
|
148
|
+
quantity: item.quantity,
|
|
149
|
+
countryCode,
|
|
150
|
+
})
|
|
151
|
+
const newUnitPrice = (newVariant as any).calculated_price?.calculated_amount ?? 0
|
|
152
|
+
trackAddToCart({
|
|
153
|
+
currency: currencyCode,
|
|
154
|
+
value: (newUnitPrice * item.quantity) / 100,
|
|
155
|
+
items: [
|
|
156
|
+
{
|
|
157
|
+
item_id: newVariant.id,
|
|
158
|
+
item_name: product?.title ?? item.title ?? "",
|
|
159
|
+
price: newUnitPrice / 100,
|
|
160
|
+
quantity: item.quantity,
|
|
161
|
+
item_variant: newVariant.title ?? undefined,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
})
|
|
165
|
+
} catch (err: any) {
|
|
166
|
+
setError(err.message)
|
|
167
|
+
// Revert options on error
|
|
168
|
+
const currentOptions = item.variant?.options
|
|
169
|
+
setOptions(currentOptions ? (optionsAsKeymap(currentOptions) ?? {}) : {})
|
|
170
|
+
} finally {
|
|
171
|
+
setUpdating(false)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const maxQuantity = useMemo(() => {
|
|
177
|
+
const variant = freshVariant || selectedVariant || item.variant
|
|
178
|
+
if (!variant) return 10
|
|
179
|
+
|
|
180
|
+
// If inventory management is off or backorders are allowed, allow a high quantity
|
|
181
|
+
if (variant.manage_inventory === false || (variant as any).allow_backorder) {
|
|
182
|
+
return 100
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Otherwise use actual inventory quantity if available
|
|
186
|
+
const stock = (variant as any).inventory_quantity
|
|
187
|
+
if (stock === undefined || stock === null) {
|
|
188
|
+
return 100 // Fallback if still unknown
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return stock
|
|
192
|
+
}, [freshVariant, selectedVariant, item.variant])
|
|
193
|
+
|
|
194
|
+
const isAtMax = item.quantity >= maxQuantity
|
|
195
|
+
|
|
196
|
+
// Get price information from cart item
|
|
197
|
+
// item.total is the total for the line item (quantity * unit_price)
|
|
198
|
+
// So we divide by quantity to get unit price
|
|
199
|
+
const unitTotal = item.total ? item.total / item.quantity : 0
|
|
200
|
+
const unitOriginalTotal = item.original_total ? item.original_total / item.quantity : 0
|
|
201
|
+
|
|
202
|
+
// Use variant price if available, otherwise use item total
|
|
203
|
+
const displayVariant = selectedVariant || item.variant
|
|
204
|
+
const variantPrice = displayVariant?.calculated_price?.calculated_amount
|
|
205
|
+
const variantOriginalPrice = displayVariant?.calculated_price?.original_amount
|
|
206
|
+
|
|
207
|
+
// Prefer variant price, fallback to item total
|
|
208
|
+
// Note: variant prices are already in smallest currency unit (e.g., paise for INR)
|
|
209
|
+
// item.total is also in smallest currency unit
|
|
210
|
+
const currentPriceAmount = variantPrice || unitTotal
|
|
211
|
+
const originalPriceAmount = variantOriginalPrice || unitOriginalTotal
|
|
212
|
+
|
|
213
|
+
const discountPercentage = originalPriceAmount && currentPriceAmount && originalPriceAmount > currentPriceAmount
|
|
214
|
+
? Math.round(((originalPriceAmount - currentPriceAmount) / originalPriceAmount) * 100)
|
|
215
|
+
: 0
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<div className="bg-surface border border-gray-200 rounded-lg p-3 min-[340px]:p-3.5 min-[550px]:p-4 sm:p-4 min-[1023px]:p-3 min-[1150px]:p-4 min-[1360px]:p-4 mb-3 min-[340px]:mb-3.5 min-[550px]:mb-4 sm:mb-4 min-[1023px]:mb-3 min-[1150px]:mb-4 min-[1360px]:mb-4 relative">
|
|
219
|
+
{/* Remove button */}
|
|
220
|
+
<div className="absolute top-2.5 min-[340px]:top-3 min-[550px]:top-3 right-2.5 min-[340px]:right-3 min-[550px]:right-3 sm:top-3 sm:right-3 min-[1023px]:top-2 min-[1023px]:right-2 min-[1150px]:top-3 min-[1150px]:right-3 min-[1360px]:top-3 min-[1360px]:right-3 z-10">
|
|
221
|
+
<DeleteButton
|
|
222
|
+
id={item.id}
|
|
223
|
+
productId={item.product_id || ""}
|
|
224
|
+
thumbnail={item.thumbnail}
|
|
225
|
+
data-testid="product-delete-button"
|
|
226
|
+
onAfterDelete={() => {
|
|
227
|
+
const unitTotal = item.total ? item.total / item.quantity : 0
|
|
228
|
+
trackRemoveFromCart({
|
|
229
|
+
currency: currencyCode,
|
|
230
|
+
value: (item.total ?? 0) / 100,
|
|
231
|
+
items: [
|
|
232
|
+
{
|
|
233
|
+
item_id: item.variant_id ?? item.id,
|
|
234
|
+
item_name: item.title ?? "",
|
|
235
|
+
price: unitTotal / 100,
|
|
236
|
+
quantity: item.quantity,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
})
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div className="flex flex-col sm:flex-row gap-3 min-[340px]:gap-3.5 min-[550px]:gap-4 sm:gap-4 min-[1023px]:gap-3 min-[1150px]:gap-4 min-[1360px]:gap-4">
|
|
245
|
+
{/* Product Image */}
|
|
246
|
+
<LocalizedClientLink
|
|
247
|
+
href={`/products/${item.product_handle}`}
|
|
248
|
+
className="flex-shrink-0 self-start"
|
|
249
|
+
>
|
|
250
|
+
<Thumbnail
|
|
251
|
+
thumbnail={item.thumbnail}
|
|
252
|
+
images={item.variant?.product?.images}
|
|
253
|
+
variantImages={item.variant?.images}
|
|
254
|
+
size="square"
|
|
255
|
+
className="w-20 h-20 min-[340px]:w-22 min-[340px]:h-22 min-[550px]:w-24 min-[550px]:h-24 sm:w-24 sm:h-24 min-[1023px]:w-20 min-[1023px]:h-20 min-[1150px]:w-24 min-[1150px]:h-24 min-[1360px]:w-24 min-[1360px]:h-24 rounded-lg object-cover"
|
|
256
|
+
/>
|
|
257
|
+
</LocalizedClientLink>
|
|
258
|
+
|
|
259
|
+
{/* Product Details */}
|
|
260
|
+
<div className="flex-1 min-w-0 flex flex-col gap-2.5 min-[340px]:gap-3 min-[550px]:gap-3 sm:gap-3 min-[1023px]:gap-2 min-[1150px]:gap-3 min-[1360px]:gap-3 pr-6 min-[340px]:pr-7 min-[550px]:pr-8 sm:pr-8 min-[1023px]:pr-6 min-[1150px]:pr-8 min-[1360px]:pr-8">
|
|
261
|
+
{/* Brand */}
|
|
262
|
+
{product?.metadata?.brand && (
|
|
263
|
+
<p className="text-xs min-[340px]:text-xs min-[550px]:text-xs text-gray-500">{product.metadata.brand}</p>
|
|
264
|
+
)}
|
|
265
|
+
|
|
266
|
+
{/* Product Title */}
|
|
267
|
+
<h3 className="font-bold text-sm min-[340px]:text-sm min-[550px]:text-base sm:text-base min-[1023px]:text-sm min-[1150px]:text-base min-[1360px]:text-base text-heading truncate pr-10 min-[340px]:pr-12 min-[550px]:pr-8 sm:pr-8 min-[1023px]:pr-8 min-[1150px]:pr-8 min-[1360px]:pr-8">
|
|
268
|
+
{item.product_title}
|
|
269
|
+
</h3>
|
|
270
|
+
|
|
271
|
+
{/* Variant Dropdowns */}
|
|
272
|
+
{!product ? (
|
|
273
|
+
<div className="flex gap-2.5 min-[340px]:gap-3 flex-wrap">
|
|
274
|
+
<div className="h-9 min-[340px]:h-9.5 min-[550px]:h-10 sm:h-10 min-[1023px]:h-9 min-[1150px]:h-10 min-[1360px]:h-10 w-full sm:w-32 min-[1023px]:w-28 min-[1150px]:w-32 min-[1360px]:w-32 bg-gray-100 animate-pulse rounded" />
|
|
275
|
+
<div className="h-9 min-[340px]:h-9.5 min-[550px]:h-10 sm:h-10 min-[1023px]:h-9 min-[1150px]:h-10 min-[1360px]:h-10 w-full sm:w-32 min-[1023px]:w-28 min-[1150px]:w-32 min-[1360px]:w-32 bg-gray-100 animate-pulse rounded" />
|
|
276
|
+
<div className="h-9 min-[340px]:h-9.5 min-[550px]:h-10 sm:h-10 min-[1023px]:h-9 min-[1150px]:h-10 min-[1360px]:h-10 w-full sm:w-24 min-[1023px]:w-20 min-[1150px]:w-24 min-[1360px]:w-24 bg-gray-100 animate-pulse rounded" />
|
|
277
|
+
</div>
|
|
278
|
+
) : (
|
|
279
|
+
<div className="flex flex-col min-[340px]:flex-col min-[640px]:flex-row gap-2.5 min-[340px]:gap-3 min-[550px]:gap-3 sm:gap-2 min-[1023px]:gap-1.5 min-[1150px]:gap-2 min-[1360px]:gap-2 items-stretch min-[640px]:items-center">
|
|
280
|
+
{productOptions.map((option: HttpTypes.StoreProductOption) => {
|
|
281
|
+
const currentValue = options[option.id]
|
|
282
|
+
const availableValues = getOptionValues(option.id)
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div key={option.id} className="relative">
|
|
286
|
+
<select
|
|
287
|
+
value={currentValue || ""}
|
|
288
|
+
onChange={(e) => handleOptionChange(option.id, e.target.value)}
|
|
289
|
+
disabled={updating}
|
|
290
|
+
className="appearance-none bg-surface border border-[var(--color-border)] rounded px-3 min-[340px]:px-3.5 min-[550px]:px-4 py-2 min-[340px]:py-2.5 min-[550px]:py-2.5 pr-8 min-[340px]:pr-9 min-[550px]:pr-9 text-xs min-[340px]:text-xs min-[550px]:text-sm sm:text-sm min-[1023px]:text-xs min-[1150px]:text-sm min-[1360px]:text-sm font-medium text-heading focus:outline-none focus:ring-2 focus:ring-brand-accent focus:border-transparent cursor-pointer disabled:opacity-50 w-full min-[640px]:min-w-[140px] min-[1023px]:min-w-[120px] min-[1150px]:min-w-[140px] min-[1360px]:min-w-[140px] h-9 min-[340px]:h-9.5 min-[550px]:h-10 sm:h-10 min-[1023px]:h-9 min-[1150px]:h-10 min-[1360px]:h-10"
|
|
291
|
+
>
|
|
292
|
+
{!currentValue && (
|
|
293
|
+
<option value="" disabled>
|
|
294
|
+
{option.title}: Select
|
|
295
|
+
</option>
|
|
296
|
+
)}
|
|
297
|
+
{availableValues.map((value) => (
|
|
298
|
+
<option key={value} value={value}>
|
|
299
|
+
{option.title}: {value}
|
|
300
|
+
</option>
|
|
301
|
+
))}
|
|
302
|
+
</select>
|
|
303
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-400">
|
|
304
|
+
<svg
|
|
305
|
+
className="fill-current h-4 w-4"
|
|
306
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
307
|
+
viewBox="0 0 20 20"
|
|
308
|
+
>
|
|
309
|
+
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
|
|
310
|
+
</svg>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
)
|
|
314
|
+
})}
|
|
315
|
+
|
|
316
|
+
{/* Quantity Selector Container with Absolute Messages */}
|
|
317
|
+
<div className="hidden min-[640px]:flex relative h-fit">
|
|
318
|
+
<div className="flex items-center border border-gray-300 rounded-[5px] overflow-hidden bg-surface h-8 min-[550px]:h-9 sm:h-10 min-[1023px]:h-9 min-[1150px]:h-10 min-[1360px]:h-10 w-[96px] min-[550px]:w-[108px] sm:w-[120px] relative z-0 shadow-sm">
|
|
319
|
+
<button
|
|
320
|
+
onClick={() => changeQuantity(Math.max(1, item.quantity - 1))}
|
|
321
|
+
disabled={updating || item.quantity <= 1}
|
|
322
|
+
className="w-8 min-[550px]:w-9 sm:w-10 h-full flex items-center justify-center text-heading hover:bg-surface-muted disabled:opacity-40 disabled:cursor-not-allowed font-semibold text-base min-[550px]:text-lg sm:text-xl transition-colors duration-200"
|
|
323
|
+
type="button"
|
|
324
|
+
>
|
|
325
|
+
−
|
|
326
|
+
</button>
|
|
327
|
+
<span className="w-8 min-[550px]:w-9 sm:w-10 text-center font-semibold text-heading border-x border-[var(--color-border)] h-full flex items-center justify-center text-xs min-[550px]:text-sm sm:text-base min-[1023px]:text-sm min-[1150px]:text-base min-[1360px]:text-base bg-surface">
|
|
328
|
+
{item.quantity}
|
|
329
|
+
</span>
|
|
330
|
+
<button
|
|
331
|
+
onClick={() => changeQuantity(Math.min(maxQuantity, item.quantity + 1))}
|
|
332
|
+
disabled={updating || item.quantity >= maxQuantity}
|
|
333
|
+
className="w-8 min-[550px]:w-9 sm:w-10 h-full flex items-center justify-center text-heading hover:bg-surface-muted disabled:opacity-40 disabled:cursor-not-allowed font-semibold text-base min-[550px]:text-lg sm:text-xl transition-colors duration-200"
|
|
334
|
+
type="button"
|
|
335
|
+
>
|
|
336
|
+
+
|
|
337
|
+
</button>
|
|
338
|
+
{updating && (
|
|
339
|
+
<div className="absolute inset-0 flex items-center justify-center bg-surface bg-opacity-75 z-10">
|
|
340
|
+
<Spinner />
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</div>
|
|
344
|
+
{isAtMax && maxQuantity > 0 && (
|
|
345
|
+
<p className="absolute top-[105%] left-0 text-[10px] min-[1150px]:text-[11px] text-red-500 font-bold animate-pulse whitespace-nowrap">
|
|
346
|
+
Maximum available stock reached
|
|
347
|
+
</p>
|
|
348
|
+
)}
|
|
349
|
+
{!isAtMax && maxQuantity < 6 && maxQuantity > 0 && (
|
|
350
|
+
<p className="absolute top-[105%] left-0 text-[10px] min-[1150px]:text-[11px] text-orange-500 font-bold whitespace-nowrap">
|
|
351
|
+
Only {maxQuantity - item.quantity} left in stock
|
|
352
|
+
</p>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
)}
|
|
357
|
+
|
|
358
|
+
{/* Quantity Selector and Price - Side by side at 340px-639px, aligned to left */}
|
|
359
|
+
<div className="flex flex-col min-[640px]:hidden gap-2 w-full mt-3 min-[340px]:mt-3.5">
|
|
360
|
+
<div className="flex flex-row gap-3 items-center justify-start w-full">
|
|
361
|
+
{/* Quantity Selector */}
|
|
362
|
+
<div className="flex items-center border border-gray-300 rounded-[5px] overflow-hidden bg-surface h-9 min-[340px]:h-9.5 min-[550px]:h-10 w-[108px] min-[340px]:w-[114px] min-[550px]:w-[120px] flex-shrink-0 relative z-0 shadow-sm">
|
|
363
|
+
<button
|
|
364
|
+
onClick={() => changeQuantity(Math.max(1, item.quantity - 1))}
|
|
365
|
+
disabled={updating || item.quantity <= 1}
|
|
366
|
+
className="w-9 min-[340px]:w-9.5 min-[550px]:w-10 h-full flex items-center justify-center text-heading hover:bg-surface-muted disabled:opacity-40 disabled:cursor-not-allowed font-semibold text-lg min-[340px]:text-xl min-[550px]:text-xl transition-colors duration-200"
|
|
367
|
+
type="button"
|
|
368
|
+
>
|
|
369
|
+
−
|
|
370
|
+
</button>
|
|
371
|
+
<span className="w-9 min-[340px]:w-9.5 min-[550px]:w-10 text-center font-semibold text-heading border-x border-[var(--color-border)] h-full flex items-center justify-center text-sm min-[340px]:text-sm min-[550px]:text-base bg-surface">
|
|
372
|
+
{item.quantity}
|
|
373
|
+
</span>
|
|
374
|
+
<button
|
|
375
|
+
onClick={() => changeQuantity(Math.min(maxQuantity, item.quantity + 1))}
|
|
376
|
+
disabled={updating || item.quantity >= maxQuantity}
|
|
377
|
+
className="w-9 min-[340px]:w-9.5 min-[550px]:w-10 h-full flex items-center justify-center text-heading hover:bg-surface-muted disabled:opacity-40 disabled:cursor-not-allowed font-semibold text-lg min-[340px]:text-xl min-[550px]:text-xl transition-colors duration-200"
|
|
378
|
+
type="button"
|
|
379
|
+
>
|
|
380
|
+
+
|
|
381
|
+
</button>
|
|
382
|
+
{updating && (
|
|
383
|
+
<div className="absolute inset-0 flex items-center justify-center bg-surface bg-opacity-75 z-10">
|
|
384
|
+
<Spinner />
|
|
385
|
+
</div>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
{/* Price Information */}
|
|
390
|
+
{currentPriceAmount > 0 && (
|
|
391
|
+
<div className="flex items-center gap-2 min-[340px]:gap-2.5 flex-wrap flex-shrink-0">
|
|
392
|
+
<span className="font-bold text-base min-[340px]:text-lg min-[550px]:text-xl text-heading whitespace-nowrap">
|
|
393
|
+
{convertToLocale({
|
|
394
|
+
amount: currentPriceAmount,
|
|
395
|
+
currency_code: currencyCode,
|
|
396
|
+
minimumFractionDigits: 0,
|
|
397
|
+
maximumFractionDigits: 0,
|
|
398
|
+
})}
|
|
399
|
+
</span>
|
|
400
|
+
{originalPriceAmount && originalPriceAmount > currentPriceAmount && (
|
|
401
|
+
<>
|
|
402
|
+
<span className="text-sm min-[340px]:text-base min-[550px]:text-lg text-gray-500 line-through whitespace-nowrap">
|
|
403
|
+
{convertToLocale({
|
|
404
|
+
amount: originalPriceAmount,
|
|
405
|
+
currency_code: currencyCode,
|
|
406
|
+
minimumFractionDigits: 0,
|
|
407
|
+
maximumFractionDigits: 0,
|
|
408
|
+
})}
|
|
409
|
+
</span>
|
|
410
|
+
{discountPercentage > 0 && (
|
|
411
|
+
<span className="text-xs min-[340px]:text-xs min-[550px]:text-sm text-brand-accent font-medium whitespace-nowrap">
|
|
412
|
+
({discountPercentage}% OFF)
|
|
413
|
+
</span>
|
|
414
|
+
)}
|
|
415
|
+
</>
|
|
416
|
+
)}
|
|
417
|
+
</div>
|
|
418
|
+
)}
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
{/* Mobile Stock Warning - Below Selector */}
|
|
422
|
+
<div className="flex flex-col gap-1.5 px-1 mt-3">
|
|
423
|
+
{isAtMax && maxQuantity > 0 && (
|
|
424
|
+
<p className="text-[10px] text-red-500 font-bold animate-pulse">
|
|
425
|
+
Maximum stock reached
|
|
426
|
+
</p>
|
|
427
|
+
)}
|
|
428
|
+
{!isAtMax && maxQuantity < 6 && maxQuantity > 0 && (
|
|
429
|
+
<p className="text-[10px] text-orange-500 font-bold">
|
|
430
|
+
Only {maxQuantity - item.quantity} left
|
|
431
|
+
</p>
|
|
432
|
+
)}
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
{/* Price Information - Only visible at 640px+ (same as 650px view) */}
|
|
437
|
+
{currentPriceAmount > 0 && (
|
|
438
|
+
<div className="hidden min-[640px]:flex items-center gap-2 flex-wrap">
|
|
439
|
+
<span className="font-bold text-base min-[550px]:text-[0.9375rem] sm:text-lg min-[1023px]:text-base min-[1150px]:text-lg min-[1360px]:text-lg text-heading">
|
|
440
|
+
{convertToLocale({
|
|
441
|
+
amount: currentPriceAmount,
|
|
442
|
+
currency_code: currencyCode,
|
|
443
|
+
minimumFractionDigits: 0,
|
|
444
|
+
maximumFractionDigits: 0,
|
|
445
|
+
})}
|
|
446
|
+
</span>
|
|
447
|
+
{originalPriceAmount && originalPriceAmount > currentPriceAmount && (
|
|
448
|
+
<>
|
|
449
|
+
<span className="text-sm min-[550px]:text-[0.8125rem] sm:text-base min-[1023px]:text-sm min-[1150px]:text-base min-[1360px]:text-base text-gray-500 line-through">
|
|
450
|
+
{convertToLocale({
|
|
451
|
+
amount: originalPriceAmount,
|
|
452
|
+
currency_code: currencyCode,
|
|
453
|
+
minimumFractionDigits: 0,
|
|
454
|
+
maximumFractionDigits: 0,
|
|
455
|
+
})}
|
|
456
|
+
</span>
|
|
457
|
+
{discountPercentage > 0 && (
|
|
458
|
+
<span className="text-xs min-[550px]:text-[0.6875rem] sm:text-sm min-[1023px]:text-xs min-[1150px]:text-sm min-[1360px]:text-sm text-brand-accent font-medium">
|
|
459
|
+
({discountPercentage}% OFF)
|
|
460
|
+
</span>
|
|
461
|
+
)}
|
|
462
|
+
</>
|
|
463
|
+
)}
|
|
464
|
+
</div>
|
|
465
|
+
)}
|
|
466
|
+
|
|
467
|
+
{/* Error Message */}
|
|
468
|
+
<ErrorMessage error={error} data-testid="product-error-message" />
|
|
469
|
+
</div>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export default CartItemCard
|
|
476
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { IconBadge, clx } from "@medusajs/ui"
|
|
4
|
+
import {
|
|
5
|
+
SelectHTMLAttributes,
|
|
6
|
+
forwardRef,
|
|
7
|
+
useEffect,
|
|
8
|
+
useImperativeHandle,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from "react"
|
|
12
|
+
|
|
13
|
+
import ChevronDown from "@modules/common/icons/chevron-down"
|
|
14
|
+
|
|
15
|
+
type NativeSelectProps = {
|
|
16
|
+
placeholder?: string
|
|
17
|
+
errors?: Record<string, unknown>
|
|
18
|
+
touched?: Record<string, unknown>
|
|
19
|
+
} & Omit<SelectHTMLAttributes<HTMLSelectElement>, "size">
|
|
20
|
+
|
|
21
|
+
const CartItemSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
|
|
22
|
+
({ placeholder = "Select...", className, children, ...props }, ref) => {
|
|
23
|
+
const innerRef = useRef<HTMLSelectElement>(null)
|
|
24
|
+
const [isPlaceholder, setIsPlaceholder] = useState(false)
|
|
25
|
+
|
|
26
|
+
useImperativeHandle<HTMLSelectElement | null, HTMLSelectElement | null>(
|
|
27
|
+
ref,
|
|
28
|
+
() => innerRef.current
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (innerRef.current && innerRef.current.value === "") {
|
|
33
|
+
setIsPlaceholder(true)
|
|
34
|
+
} else {
|
|
35
|
+
setIsPlaceholder(false)
|
|
36
|
+
}
|
|
37
|
+
}, [innerRef.current?.value])
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div>
|
|
41
|
+
<IconBadge
|
|
42
|
+
onFocus={() => innerRef.current?.focus()}
|
|
43
|
+
onBlur={() => innerRef.current?.blur()}
|
|
44
|
+
className={clx(
|
|
45
|
+
"relative flex items-center txt-compact-small border text-ui-fg-base group",
|
|
46
|
+
className,
|
|
47
|
+
{
|
|
48
|
+
"text-ui-fg-subtle": isPlaceholder,
|
|
49
|
+
}
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
<select
|
|
53
|
+
ref={innerRef}
|
|
54
|
+
{...props}
|
|
55
|
+
className="appearance-none bg-transparent border-none px-4 transition-colors duration-150 focus:border-gray-700 outline-none w-16 h-16 items-center justify-center"
|
|
56
|
+
>
|
|
57
|
+
<option disabled value="">
|
|
58
|
+
{placeholder}
|
|
59
|
+
</option>
|
|
60
|
+
{children}
|
|
61
|
+
</select>
|
|
62
|
+
<span className="absolute flex pointer-events-none justify-end w-8 group-hover:animate-pulse">
|
|
63
|
+
<ChevronDown />
|
|
64
|
+
</span>
|
|
65
|
+
</IconBadge>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
CartItemSelect.displayName = "CartItemSelect"
|
|
72
|
+
|
|
73
|
+
export default CartItemSelect
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react"
|
|
4
|
+
import { trackViewCart } from "@core/analytics/ga4-ecommerce"
|
|
5
|
+
|
|
6
|
+
type CartViewTrackerProps = {
|
|
7
|
+
cart: {
|
|
8
|
+
items?: Array<{
|
|
9
|
+
id: string
|
|
10
|
+
variant_id?: string
|
|
11
|
+
title?: string
|
|
12
|
+
quantity: number
|
|
13
|
+
total?: number
|
|
14
|
+
}>
|
|
15
|
+
total?: number
|
|
16
|
+
currency_code?: string
|
|
17
|
+
region?: { currency_code?: string }
|
|
18
|
+
} | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function CartViewTracker({ cart }: CartViewTrackerProps) {
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const currency =
|
|
24
|
+
cart?.currency_code ?? (cart as any)?.region?.currency_code
|
|
25
|
+
if (!cart?.items?.length || !currency) return
|
|
26
|
+
|
|
27
|
+
const currencyUpper = currency.toUpperCase()
|
|
28
|
+
const value = (cart.total ?? 0) / 100
|
|
29
|
+
const items = cart.items.map((item, index) => {
|
|
30
|
+
const unitTotal = item.total && item.quantity ? item.total / item.quantity : 0
|
|
31
|
+
return {
|
|
32
|
+
item_id: item.variant_id ?? item.id,
|
|
33
|
+
item_name: item.title ?? "",
|
|
34
|
+
price: unitTotal / 100,
|
|
35
|
+
quantity: item.quantity,
|
|
36
|
+
index,
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
trackViewCart({ currency: currencyUpper, value, items })
|
|
41
|
+
}, [cart])
|
|
42
|
+
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { HttpTypes } from "@medusajs/types"
|
|
4
|
+
import { useState, useEffect } from "react"
|
|
5
|
+
import { getShiprocketServiceability } from "@core/data/fulfillment"
|
|
6
|
+
import { Truck } from "lucide-react"
|
|
7
|
+
|
|
8
|
+
interface DeliveryEstimate {
|
|
9
|
+
etd: string | null
|
|
10
|
+
isLoading: boolean
|
|
11
|
+
error: string | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const CartDeliveryEstimate = ({ cart }: { cart: HttpTypes.StoreCart | null }) => {
|
|
15
|
+
const [estimate, setEstimate] = useState<DeliveryEstimate>({
|
|
16
|
+
etd: null,
|
|
17
|
+
isLoading: false,
|
|
18
|
+
error: null,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const postalCode = cart?.shipping_address?.postal_code
|
|
22
|
+
const firstItem = cart?.items?.[0]
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const fetchEstimate = async () => {
|
|
26
|
+
// Prioritize metadata if available
|
|
27
|
+
if (cart?.metadata?.etd) {
|
|
28
|
+
setEstimate({ etd: cart.metadata.etd as string, isLoading: false, error: null })
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!postalCode || postalCode.length < 6 || !firstItem?.variant_id) {
|
|
33
|
+
setEstimate({ etd: null, isLoading: false, error: null })
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
setEstimate(prev => ({ ...prev, isLoading: true, error: null }))
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const data = await getShiprocketServiceability(postalCode, firstItem.variant_id, 0)
|
|
41
|
+
|
|
42
|
+
let etd = null
|
|
43
|
+
if (data?.etd) etd = data.etd
|
|
44
|
+
else if (data?.data?.etd) etd = data.data.etd
|
|
45
|
+
else if (data?.data?.available_courier_companies?.length > 0) etd = data.data.available_courier_companies[0].etd
|
|
46
|
+
|
|
47
|
+
if (etd) {
|
|
48
|
+
setEstimate({ etd, isLoading: false, error: null })
|
|
49
|
+
document.cookie = `_medusa_last_etd=${etd}; path=/; max-age=3600`
|
|
50
|
+
} else {
|
|
51
|
+
setEstimate({ etd: null, isLoading: false, error: "Not available" })
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
setEstimate({ etd: null, isLoading: false, error: "Error fetching" })
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fetchEstimate()
|
|
59
|
+
}, [postalCode, firstItem?.variant_id, cart?.metadata?.etd])
|
|
60
|
+
|
|
61
|
+
if (!postalCode || postalCode.length < 6) return null
|
|
62
|
+
|
|
63
|
+
const formatDeliveryDate = (etd: string) => {
|
|
64
|
+
const date = new Date(etd)
|
|
65
|
+
return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="bg-white border border-gray-200 rounded-lg p-4 mb-4 shadow-sm flex items-center gap-4">
|
|
70
|
+
<div className="w-10 h-10 rounded-full bg-purple-50 flex items-center justify-center flex-shrink-0">
|
|
71
|
+
<Truck className="w-5 h-5 text-[#8B5AB1]" />
|
|
72
|
+
</div>
|
|
73
|
+
<div>
|
|
74
|
+
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-0.5">Delivery Estimate</p>
|
|
75
|
+
{estimate.isLoading ? (
|
|
76
|
+
<p className="text-sm text-gray-500 animate-pulse">Calculating...</p>
|
|
77
|
+
) : estimate.etd ? (
|
|
78
|
+
<p className="text-sm sm:text-base text-gray-900 font-medium">
|
|
79
|
+
Estimated delivery by <span className="font-bold text-[#8B5AB1]">{formatDeliveryDate(estimate.etd)}</span>
|
|
80
|
+
</p>
|
|
81
|
+
) : (
|
|
82
|
+
<p className="text-sm text-gray-500">Enter a valid pincode for estimate</p>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default CartDeliveryEstimate
|