@mframework/layer-commerce 0.0.7 → 0.0.8
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/app/components/catalog/product/ProductAccordion/ProductAccordion.vue +41 -0
- package/app/components/catalog/product/ProductAccordion/__tests__/ProductAccordion.spec.ts +15 -0
- package/app/components/catalog/product/ProductAccordion/types.ts +5 -0
- package/app/components/catalog/product/ProductProperties/ProductProperties.vue +52 -0
- package/app/components/catalog/product/ProductProperties/__tests__/ProductProperties.spec.ts +15 -0
- package/app/components/catalog/product/ProductProperties/types.ts +5 -0
- package/app/components/catalog/product/ProductSlider/ProductSlider.vue +28 -0
- package/app/components/catalog/product/ProductSlider/__tests__/ProductSlider.spec.ts +14 -0
- package/app/components/catalog/product/ProductSlider/types.ts +7 -0
- package/app/components/catalog/product/RecommendedProducts/RecommendedProducts.vue +12 -0
- package/app/components/catalog/product/RecommendedProducts/types.ts +5 -0
- package/app/components/catalog/product/RenderContentProductSlider/RenderContentProductSlider.vue +11 -0
- package/app/components/catalog/product/add-attribute.vue +54 -0
- package/app/components/catalog/product/add-product-type.vue +54 -0
- package/app/components/catalog/product/add-product.vue +53 -0
- package/app/components/catalog/product/add-showcase.vue +52 -0
- package/app/components/catalog/product/add-station.vue +54 -0
- package/app/components/catalog/product/bestsellers.vue +69 -0
- package/app/components/catalog/product/bidding.vue +93 -0
- package/app/components/catalog/product/colorOptions.vue +58 -0
- package/app/components/catalog/product/deals.vue +61 -0
- package/app/components/catalog/product/exclusives.vue +58 -0
- package/app/components/catalog/product/featuredproducts.vue +69 -0
- package/app/components/catalog/product/giftCard.vue +63 -0
- package/app/components/catalog/product/latestproducts.vue +58 -0
- package/app/components/catalog/product/productCard.vue +71 -0
- package/app/components/catalog/product/productCompare.vue +60 -0
- package/app/components/catalog/product/productCompareTable.vue +441 -0
- package/app/components/catalog/product/productDetails.vue +120 -0
- package/app/components/catalog/product/productFaqs.vue +17 -0
- package/app/components/catalog/product/productGallery.vue +16 -0
- package/app/components/catalog/product/productQty.vue +54 -0
- package/app/components/catalog/product/productReviews.vue +56 -0
- package/app/components/catalog/product/productSpecs.vue +116 -0
- package/app/components/catalog/product/radiostation.vue +36 -0
- package/app/components/catalog/product/recentlyviewed.vue +43 -0
- package/app/components/catalog/product/relatedbrands.vue +54 -0
- package/app/components/catalog/product/relatedproducts.vue +58 -0
- package/app/components/catalog/product/relatedstations.vue +40 -0
- package/app/components/catalog/product/shippingOptions.vue +41 -0
- package/app/components/catalog/product/sizeOptions.vue +47 -0
- package/app/components/catalog/product/update-attribute-set.vue +209 -0
- package/app/components/catalog/product/update-attribute.vue +118 -0
- package/app/components/catalog/product/update-product.vue +372 -0
- package/app/components/catalog/product/update-showcase.vue +153 -0
- package/app/components/catalog/shops/relatedstores.vue +52 -0
- package/app/components/catalog/shops/restaurant.vue +66 -0
- package/app/components/catalog/shops/stores.vue +44 -0
- package/app/components/catalog/vendor/README.md +3 -0
- package/app/components/catalog/vendor/blocks/biggestcustomers.vue +33 -0
- package/app/components/catalog/vendor/blocks/lowestselling.vue +33 -0
- package/app/components/catalog/vendor/blocks/topcategories.vue +33 -0
- package/app/components/catalog/vendor/blocks/topproducts.vue +27 -0
- package/app/components/catalog/vendor/pages/attributes.vue +43 -0
- package/app/components/catalog/vendor/pages/commissions.vue +43 -0
- package/app/components/catalog/vendor/pages/crm.vue +67 -0
- package/app/components/catalog/vendor/pages/dashboard.vue +46 -0
- package/app/components/catalog/vendor/pages/emails.vue +43 -0
- package/app/components/catalog/vendor/pages/enquiries.vue +43 -0
- package/app/components/catalog/vendor/pages/invoices.vue +43 -0
- package/app/components/catalog/vendor/pages/orders.vue +68 -0
- package/app/components/catalog/vendor/pages/products.vue +55 -0
- package/app/components/catalog/vendor/pages/reviews.vue +48 -0
- package/app/components/catalog/vendor/pages/shipments.vue +43 -0
- package/app/components/catalog/vendor/pages/stores.vue +43 -0
- package/app/components/content/blocks/breadcrumbs.vue +0 -0
- package/app/components/content/blocks/currencySwitcher.vue +0 -0
- package/app/components/content/blocks/languageSwitcher.vue +0 -0
- package/app/components/content/blocks/videoproduct.vue +9 -0
- package/app/components/content/pages/checkout.vue +118 -0
- package/app/components/content/pages/meeoviGlobal.vue +68 -0
- package/app/components/content/pages/pickup-locations.vue +238 -0
- package/app/components/content/pages/showcases.vue +90 -0
- package/app/components/content/pages/success.vue +60 -0
- package/app/components/marketing/add-brand.vue +54 -0
- package/app/components/marketing/add-incentive.vue +54 -0
- package/app/components/marketing/promotions/giftcards.vue +127 -0
- package/app/components/marketing/promotions/subscriptions.vue +134 -0
- package/app/components/marketing/update-incentive.vue +326 -0
- package/app/components/menus/lowernav.vue +78 -0
- package/app/components/partials/LocaleSelector.vue +24 -0
- package/app/components/partials/ShoppingCart.vue +128 -0
- package/app/components/partials/StripePayment.vue +149 -0
- package/app/components/partials/addToCartBtn.vue +40 -0
- package/app/components/partials/cartItem.vue +124 -0
- package/app/components/partials/checkoutButton.vue +44 -0
- package/app/components/partials/compareBtn.vue +68 -0
- package/app/components/partials/ratings.vue +13 -0
- package/app/components/partials/store/CurrencySelector.vue +133 -0
- package/app/components/partials/store/StoreSwitcher.vue +13 -0
- package/app/components/related/brandCard.vue +41 -0
- package/app/components/related/incentiveCard.vue +44 -0
- package/app/components/related/invoiceCard.vue +43 -0
- package/app/components/related/orderCard.vue +43 -0
- package/app/components/related/relatedproducts.vue +17 -0
- package/app/components/sales/CartPageContent/CartPageContent.vue +43 -0
- package/app/components/sales/CheckoutAddress/CheckoutAddress.vue +50 -0
- package/app/components/sales/CheckoutAddress/__tests__/CheckoutAddress.spec.ts +16 -0
- package/app/components/sales/CheckoutAddress/types.ts +16 -0
- package/app/components/sales/CheckoutPayment/CheckoutPayment.vue +65 -0
- package/app/components/sales/CheckoutPayment/__tests__/CheckoutPayment.spec.ts +14 -0
- package/app/components/sales/CheckoutPayment/types.ts +12 -0
- package/app/components/sales/OrderSummary/OrderSummary.vue +57 -0
- package/app/components/sales/OrderSummary/__tests__/ContactInformation.spec.ts +52 -0
- package/app/components/sales/OrderSummary/types.ts +5 -0
- package/app/components/sales/incentives.vue +247 -0
- package/app/components/sales/invoices.vue +107 -0
- package/app/components/sales/orders.vue +378 -0
- package/app/components/sales/shipments.vue +65 -0
- package/app/components/sales/transactions.vue +109 -0
- package/app/components/shop/add-shop.vue +54 -0
- package/app/components/shop/cart/cartItem.vue +182 -0
- package/app/components/shop/cart/checkout.vue +415 -0
- package/app/components/shop/checkout/StripeCardElement.vue +206 -0
- package/app/components/shop/checkout/StripeCheckout.vue +49 -0
- package/app/components/shop/checkout/addressBilling.vue +263 -0
- package/app/components/shop/checkout/addressShipping.vue +175 -0
- package/app/components/shop/checkout/cart/ProductItem.vue +56 -0
- package/app/components/shop/checkout/cart/PromotionItem.vue +53 -0
- package/app/stores/cart.ts +1 -1
- package/app/types/Direction.type.ts +1 -1
- package/app/types/Global.type.ts +6 -6
- package/app/types/Layout.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/Cart.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Cart.type.ts +2 -2
- package/app/{normalizers → types/normalizers}/Checkout.query.ts +2 -2
- package/app/{normalizers → types/normalizers}/Config.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Config.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/ContactForm.query.ts +2 -2
- package/app/{normalizers → types/normalizers}/CreditMemo.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/GiftCard.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/Invoice.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/MyAccount.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/MyAccount.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/NewsletterSubscription.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Order.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Order.type.ts +2 -2
- package/app/{normalizers → types/normalizers}/Payment.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/ProductCompare.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/ProductCompare.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/ProductList.query.ts +2 -2
- package/app/{normalizers → types/normalizers}/ProductList.type.ts +2 -2
- package/app/{normalizers → types/normalizers}/Return.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/Review.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Review.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/StoreInPickUp.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/Subscription.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/Transaction.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/UrlRewrites.query.ts +1 -1
- package/app/{normalizers → types/normalizers}/UrlRewrites.type.ts +1 -1
- package/app/{normalizers → types/normalizers}/Wishlist.query.ts +4 -4
- package/app/{normalizers → types/normalizers}/Wishlist.type.ts +1 -1
- package/app/utils/Address/Address.type.ts +1 -1
- package/app/utils/Address/index.ts +5 -5
- package/app/utils/Cart/Cart.ts +1 -1
- package/app/utils/Currency/Currency.ts +1 -1
- package/app/utils/History/History.type.ts +1 -1
- package/app/utils/Menu/Menu.ts +1 -1
- package/app/utils/Menu/Menu.type.ts +2 -2
- package/app/utils/Orders/Orders.ts +1 -1
- package/app/utils/Preload/CategoryPreload.ts +2 -2
- package/app/utils/Preload/ProductPreload.ts +1 -1
- package/app/utils/Preload/index.ts +1 -1
- package/app/utils/Price/Price.ts +1 -1
- package/app/utils/Product/Extract.ts +1 -1
- package/app/utils/Product/Product.ts +1 -1
- package/app/utils/Product/Product.type.ts +1 -1
- package/app/utils/Product/Transform.ts +1 -1
- package/app/utils/Wishlist/Wishlist.ts +1 -1
- package/app/utils/client.ts +19 -19
- package/package.json +1 -2
- package/tsconfig.json +2 -2
- package/app/cart/useCart.ts +0 -1
- /package/app/{components → composables}/ChevronIcon/ChevronIcon.config.ts +0 -0
- /package/app/{components → composables}/DateSelect/DateSelect.config.ts +0 -0
- /package/app/{components → composables}/Field/Field.config.ts +0 -0
- /package/app/{components → composables}/FieldDate/FieldDate.config.ts +0 -0
- /package/app/{components → composables}/Form/Form.type.ts +0 -0
- /package/app/{components → composables}/Product/Product.config.ts +0 -0
- /package/app/{components → composables}/Product/Stock.config.ts +0 -0
- /package/app/{components → composables}/ProductCustomizableOption/ProductCustomizableOption.config.ts +0 -0
- /package/app/{components → composables}/ProductGallery/ProductGallery.config.ts +0 -0
- /package/app/{components → composables}/ProductReviews/ProductReviews.config.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Category.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Category.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/CheckEmail.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Checkout.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/CmsBlock.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/CmsBlock.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/CmsPage.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/CmsPage.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Menu.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Menu.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/ProductAlerts.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Region.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Region.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Slider.query.ts +0 -0
- /package/app/{normalizers → types/normalizers}/Slider.type.ts +0 -0
- /package/app/{normalizers → types/normalizers}/StoreInPickUp.type.ts +0 -0
- /package/app/{routes → types/routes}/CategoryPage/CategoryPage.config.ts +0 -0
- /package/app/{routes → types/routes}/CategoryPage/CategoryPage.type.ts +0 -0
- /package/app/{routes → types/routes}/Checkout/Checkout.config.ts +0 -0
- /package/app/{routes → types/routes}/Checkout/Checkout.type.ts +0 -0
- /package/app/{routes → types/routes}/MyAccount/MyAccount.config.ts +0 -0
- /package/app/{routes → types/routes}/SearchPage/SearchPage.config.ts +0 -0
- /package/app/{routes → types/routes}/UrlRewrites/UrlRewrites.config.ts +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<!-- components/StripePayment.vue -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="stripe-payment-container">
|
|
4
|
+
<div v-if="error" class="error-message">
|
|
5
|
+
{{ error }}
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div v-if="stripe && clientSecret" class="payment-form">
|
|
9
|
+
<form @submit.prevent="handleSubmit">
|
|
10
|
+
<div ref="paymentElement"></div>
|
|
11
|
+
|
|
12
|
+
<button type="submit" class="payment-button" :disabled="!stripe || loading">
|
|
13
|
+
<span v-if="loading">Processing...</span>
|
|
14
|
+
<span v-else>Pay Now</span>
|
|
15
|
+
</button>
|
|
16
|
+
</form>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import {
|
|
23
|
+
ref,
|
|
24
|
+
onMounted
|
|
25
|
+
} from 'vue'
|
|
26
|
+
import type {
|
|
27
|
+
Stripe,
|
|
28
|
+
StripeElements
|
|
29
|
+
} from '@stripe/stripe-js'
|
|
30
|
+
import {
|
|
31
|
+
loadStripe
|
|
32
|
+
} from '@stripe/stripe-js'
|
|
33
|
+
|
|
34
|
+
const props = defineProps < {
|
|
35
|
+
orderCode: string,
|
|
36
|
+
clientSecret: string
|
|
37
|
+
} > ()
|
|
38
|
+
|
|
39
|
+
const stripe = ref < Stripe | null > (null)
|
|
40
|
+
const elements = ref < StripeElements | null > (null)
|
|
41
|
+
const error = ref < string | null > (null)
|
|
42
|
+
const loading = ref(false)
|
|
43
|
+
const paymentElement = ref < HTMLDivElement | null > (null)
|
|
44
|
+
const clientSecret = ref < string > (props.clientSecret)
|
|
45
|
+
|
|
46
|
+
onMounted(async () => {
|
|
47
|
+
try {
|
|
48
|
+
stripe.value = await loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY)
|
|
49
|
+
if (stripe.value && clientSecret.value) {
|
|
50
|
+
elements.value = stripe.value.elements({
|
|
51
|
+
clientSecret: clientSecret.value,
|
|
52
|
+
appearance: {
|
|
53
|
+
theme: 'stripe',
|
|
54
|
+
variables: {
|
|
55
|
+
colorPrimary: '#0570de',
|
|
56
|
+
colorBackground: '#ffffff',
|
|
57
|
+
colorText: '#30313d'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Create and mount payment element
|
|
63
|
+
if (elements.value && paymentElement.value) {
|
|
64
|
+
const element = elements.value.create('payment')
|
|
65
|
+
element.mount(paymentElement.value as HTMLDivElement)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
error.value = 'Failed to initialize Stripe'
|
|
70
|
+
console.error('Stripe initialization error:', err)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const handleSubmit = async () => {
|
|
75
|
+
if (!stripe.value || !elements.value) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
loading.value = true
|
|
81
|
+
error.value = null
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
error: stripeError
|
|
85
|
+
} = await stripe.value.confirmPayment({
|
|
86
|
+
elements: elements.value,
|
|
87
|
+
confirmParams: {
|
|
88
|
+
return_url: `${window.location.origin}/checkout/confirmation/${props.orderCode}`,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (stripeError) {
|
|
93
|
+
error.value = stripeError.message || 'Payment failed'
|
|
94
|
+
}
|
|
95
|
+
} catch (err) {
|
|
96
|
+
error.value = 'An unexpected error occurred'
|
|
97
|
+
console.error('Payment error:', err)
|
|
98
|
+
} finally {
|
|
99
|
+
loading.value = false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<style scoped>
|
|
105
|
+
.stripe-payment-container {
|
|
106
|
+
max-width: 500px;
|
|
107
|
+
margin: 0 auto;
|
|
108
|
+
padding: 20px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.error-message {
|
|
112
|
+
color: #df1b41;
|
|
113
|
+
background-color: #fff0f0;
|
|
114
|
+
padding: 12px;
|
|
115
|
+
border-radius: 4px;
|
|
116
|
+
margin-bottom: 16px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.payment-form {
|
|
120
|
+
background: #ffffff;
|
|
121
|
+
padding: 20px;
|
|
122
|
+
border-radius: 4px;
|
|
123
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.payment-button {
|
|
127
|
+
background: #5469d4;
|
|
128
|
+
color: #ffffff;
|
|
129
|
+
border: none;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
padding: 12px 16px;
|
|
132
|
+
font-size: 16px;
|
|
133
|
+
font-weight: 600;
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
display: block;
|
|
136
|
+
width: 100%;
|
|
137
|
+
margin-top: 24px;
|
|
138
|
+
transition: all 0.2s ease;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.payment-button:disabled {
|
|
142
|
+
opacity: 0.5;
|
|
143
|
+
cursor: not-allowed;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.payment-button:hover:not(:disabled) {
|
|
147
|
+
filter: brightness(1.1);
|
|
148
|
+
}
|
|
149
|
+
</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col gap-2">
|
|
3
|
+
<v-btn size="lg" class="w-full xs:ml-4" :disabled="loading || hasItemInCart" @click="addToCart">
|
|
4
|
+
<template #prefix>
|
|
5
|
+
<SfIconShoppingCart size="sm" />
|
|
6
|
+
</template>
|
|
7
|
+
Add to Cart
|
|
8
|
+
</v-btn>
|
|
9
|
+
<compareBtn />
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
import { SfIconShoppingCart } from "@storefront-ui/vue";
|
|
15
|
+
import compareBtn from './compareBtn.vue';
|
|
16
|
+
import { useCartStore } from '~/stores/cart'
|
|
17
|
+
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
product: {
|
|
20
|
+
type: Object,
|
|
21
|
+
required: true
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const cart = useCartStore()
|
|
26
|
+
|
|
27
|
+
const addToCart = async (product) => {
|
|
28
|
+
try {
|
|
29
|
+
await cart.addItem({
|
|
30
|
+
sku: product.sku,
|
|
31
|
+
name: product.name,
|
|
32
|
+
price: product.price,
|
|
33
|
+
qty: 1
|
|
34
|
+
})
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// Handle error (show notification, etc.)
|
|
37
|
+
console.error('Failed to add item to cart:', error)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex items-center py-4 border-b border-gray-200">
|
|
3
|
+
<div class="shrink-0 w-24 h-24">
|
|
4
|
+
<productCard :product="productForCard" />
|
|
5
|
+
</div>
|
|
6
|
+
<div class="ml-4 flex-1">
|
|
7
|
+
<h3 class="text-lg font-medium text-gray-900">{{ item.productVariant?.name }}</h3>
|
|
8
|
+
<p class="mt-1 text-sm text-gray-500">SKU: {{ item.productVariant?.sku }}</p>
|
|
9
|
+
<div class="mt-2 flex items-center">
|
|
10
|
+
<div class="flex items-center border border-gray-300 rounded">
|
|
11
|
+
<button
|
|
12
|
+
class="px-2 py-1 text-gray-600 hover:bg-gray-100"
|
|
13
|
+
@click="updateQuantity(item.id, item.quantity - 1)"
|
|
14
|
+
:disabled="item.quantity <= 1"
|
|
15
|
+
>
|
|
16
|
+
-
|
|
17
|
+
</button>
|
|
18
|
+
<span class="px-4 py-1">{{ item.quantity }}</span>
|
|
19
|
+
<button
|
|
20
|
+
class="px-2 py-1 text-gray-600 hover:bg-gray-100"
|
|
21
|
+
@click="updateQuantity(item.id, item.quantity + 1)"
|
|
22
|
+
>
|
|
23
|
+
+
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
<button
|
|
27
|
+
class="ml-4 text-sm text-red-600 hover:text-red-800"
|
|
28
|
+
@click="removeItem(item.id)"
|
|
29
|
+
>
|
|
30
|
+
Remove
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="ml-4 text-right">
|
|
35
|
+
<p class="text-lg font-medium text-gray-900">
|
|
36
|
+
{{ formatPrice(item.unitPriceWithTax || productForCardPrice) }}
|
|
37
|
+
</p>
|
|
38
|
+
<p v-if="item.listPriceWithTax && item.listPriceWithTax !== item.unitPriceWithTax" class="mt-1 text-sm text-gray-500 line-through">
|
|
39
|
+
{{ formatPrice(item.listPriceWithTax) }}
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
import productCard from '../catalog/product/productCard.vue';
|
|
47
|
+
import { computed } from 'vue';
|
|
48
|
+
|
|
49
|
+
const props = defineProps<{ item: Record<string, any> }>();
|
|
50
|
+
const emit = defineEmits(['cart-changed'])
|
|
51
|
+
const nuxtApp = useNuxtApp()
|
|
52
|
+
|
|
53
|
+
// Prepare a product object compatible with productCard.vue
|
|
54
|
+
const productForCard = computed(() => {
|
|
55
|
+
const pv = props.item?.productVariant || {};
|
|
56
|
+
return {
|
|
57
|
+
id: pv?.product?.id || pv?.id || props.item?.id,
|
|
58
|
+
name: pv?.name || pv?.product?.name,
|
|
59
|
+
image: pv?.featuredAsset ? { filename_disk: pv.featuredAsset?.id } : (pv?.product?.image || {}),
|
|
60
|
+
brands: pv?.product?.brands || [],
|
|
61
|
+
currency: pv?.product?.currency || [],
|
|
62
|
+
price: (props.item?.unitPriceWithTax && props.item.unitPriceWithTax / 100) || pv?.price || 0,
|
|
63
|
+
rating: pv?.product?.rating || 0,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const productForCardPrice = computed(() => productForCard.value?.price || 0);
|
|
68
|
+
|
|
69
|
+
async function removeItem(orderLineId: string) {
|
|
70
|
+
try {
|
|
71
|
+
// Try to delete using Directus client
|
|
72
|
+
try {
|
|
73
|
+
if (nuxtApp.$directus && nuxtApp.$directus.items) {
|
|
74
|
+
await nuxtApp.$directus.items('order_lines').delete(orderLineId);
|
|
75
|
+
} else if (nuxtApp.$deleteItem) {
|
|
76
|
+
await nuxtApp.$deleteItem('order_lines', orderLineId);
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.warn('Directus delete failed, falling back to plugin helper or ignoring', e);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
emit('cart-changed');
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('Failed to remove item:', error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function updateQuantity(orderLineId: string, newQuantity: number) {
|
|
89
|
+
if (newQuantity < 1) return;
|
|
90
|
+
try {
|
|
91
|
+
// Try to update via Directus SDK
|
|
92
|
+
try {
|
|
93
|
+
if (nuxtApp.$directus && nuxtApp.$directus.items) {
|
|
94
|
+
await nuxtApp.$directus.items('order_lines').update(orderLineId, { quantity: newQuantity });
|
|
95
|
+
} else if (nuxtApp.$createItem) {
|
|
96
|
+
// Fallback: read existing, delete and recreate with new quantity
|
|
97
|
+
const existing = await nuxtApp.$readItem('order_lines', orderLineId).catch(() => null);
|
|
98
|
+
if (existing) {
|
|
99
|
+
await nuxtApp.$deleteItem('order_lines', orderLineId).catch(() => null);
|
|
100
|
+
const payload = { ...existing, quantity: newQuantity };
|
|
101
|
+
delete payload.id;
|
|
102
|
+
await nuxtApp.$createItem('order_lines', payload).catch(() => null);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.warn('Directus update fallback failed', e);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
emit('cart-changed');
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error('Failed to update quantity:', error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatPrice(price: number) {
|
|
116
|
+
const p = price ?? 0;
|
|
117
|
+
// If price seems to be in cents (large integer), convert
|
|
118
|
+
const normalized = p > 1000 ? p / 100 : p;
|
|
119
|
+
return new Intl.NumberFormat('en-US', {
|
|
120
|
+
style: 'currency',
|
|
121
|
+
currency: 'USD'
|
|
122
|
+
}).format(normalized);
|
|
123
|
+
}
|
|
124
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<v-btn color="primary" @click="redirectToCheckout">
|
|
4
|
+
Proceed to Payment
|
|
5
|
+
</v-btn>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup>
|
|
10
|
+
import getActiveOrderQuery from '#graphql/app/commerce/queries/activeOrder.gql';
|
|
11
|
+
|
|
12
|
+
const nuxtApp = useNuxtApp();
|
|
13
|
+
|
|
14
|
+
const redirectToCheckout = async () => {
|
|
15
|
+
try {
|
|
16
|
+
// Fetch the active order to get the order code or id
|
|
17
|
+
const orderRes = await nuxtApp.$graphql.request(getActiveOrderQuery);
|
|
18
|
+
const order = orderRes?.activeOrder;
|
|
19
|
+
if (!order) return;
|
|
20
|
+
|
|
21
|
+
// Attempt to create a checkout session in Directus
|
|
22
|
+
try {
|
|
23
|
+
const payload = {
|
|
24
|
+
orderCode: order.code,
|
|
25
|
+
items: (order.lines || []).map((l) => ({ sku: l?.productVariant?.sku, quantity: l.quantity }))
|
|
26
|
+
};
|
|
27
|
+
const created = await nuxtApp.$createItem('checkout_sessions', payload);
|
|
28
|
+
// support common response shapes
|
|
29
|
+
const redirectUrl = created?.redirect_url || created?.data?.redirect_url || created?.url || created?.data?.url;
|
|
30
|
+
if (redirectUrl) {
|
|
31
|
+
window.location.href = redirectUrl;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.warn('Directus checkout session creation failed, falling back:', e);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fallback: redirect to local confirmation route
|
|
39
|
+
window.location.href = `/checkout/confirmation/${order.code}`;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('Checkout redirect failed:', error);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<SfButton size="sm" variant="tertiary" @click="handleCompare" :disabled="isInCompare">
|
|
3
|
+
<template #prefix>
|
|
4
|
+
<SfIconCompareArrows size="sm" />
|
|
5
|
+
</template>
|
|
6
|
+
{{ buttonText }}
|
|
7
|
+
</SfButton>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { computed } from 'vue';
|
|
12
|
+
import { useCompareStore } from '../../stores/compare';
|
|
13
|
+
import { useNuxtApp } from '#imports';
|
|
14
|
+
import type { Product } from '../../types/product';
|
|
15
|
+
|
|
16
|
+
// Define props
|
|
17
|
+
const props = defineProps<{ product: Product }>();
|
|
18
|
+
|
|
19
|
+
const compareStore = useCompareStore();
|
|
20
|
+
const { $directus } = useNuxtApp() as any;
|
|
21
|
+
|
|
22
|
+
// Check if the product is already in compare list
|
|
23
|
+
const isInCompare = computed(() => {
|
|
24
|
+
return compareStore.getComparedProducts.some((product: { sku: string; }) => product.sku === props.product?.sku) || compareStore.getComparedProductSkus.includes(props.product?.sku);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Dynamically update button text
|
|
28
|
+
const buttonText = computed(() => (isInCompare.value ? 'In Compare List' : 'Add to Compare'));
|
|
29
|
+
|
|
30
|
+
// Handle Add/Remove from Compare list using Directus where possible
|
|
31
|
+
const handleCompare = async () => {
|
|
32
|
+
try {
|
|
33
|
+
if (!props.product || !props.product.sku) {
|
|
34
|
+
throw new Error('Product data is required');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const sku = props.product.sku;
|
|
38
|
+
|
|
39
|
+
if (isInCompare.value) {
|
|
40
|
+
// Remove from Directus compare_items collection if exists
|
|
41
|
+
try {
|
|
42
|
+
const itemsRes = await $directus.$readItems('compare_items', { filter: { sku: { _eq: sku } } });
|
|
43
|
+
const items = itemsRes?.data || itemsRes || [];
|
|
44
|
+
for (const it of items) {
|
|
45
|
+
const id = it.id || it._id || it.ID;
|
|
46
|
+
if (id) await $directus.$deleteItem('compare_items', id);
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// ignore Directus errors, still update local store
|
|
50
|
+
console.warn('Directus remove compare item failed:', e);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
compareStore.removeComparedProduct(sku);
|
|
54
|
+
} else {
|
|
55
|
+
// Add to Directus compare_items collection
|
|
56
|
+
try {
|
|
57
|
+
await $directus.$createItem('compare_items', { sku });
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.warn('Directus create compare item failed:', e);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
compareStore.addComparedProductSku(sku);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error handling compare:', error);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
</script>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="currency-selector">
|
|
3
|
+
<select v-model="selectedCurrency" @change="handleCurrencyChange" class="currency-select">
|
|
4
|
+
<option v-for="currency in availableCurrencies" :key="currency.code" :value="currency.code">
|
|
5
|
+
{{ currency.symbol }} {{ currency.code }}
|
|
6
|
+
</option>
|
|
7
|
+
</select>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import {
|
|
13
|
+
ref,
|
|
14
|
+
onMounted,
|
|
15
|
+
watch
|
|
16
|
+
} from 'vue'
|
|
17
|
+
import {
|
|
18
|
+
useCurrencyStore
|
|
19
|
+
} from '~/stores/currency'
|
|
20
|
+
import {
|
|
21
|
+
useQuery,
|
|
22
|
+
useMutation
|
|
23
|
+
} from '@vue/apollo-composable'
|
|
24
|
+
import {
|
|
25
|
+
CURRENCY_QUERY
|
|
26
|
+
} from '#graphql/commerce/queries/currency.gql'
|
|
27
|
+
import {
|
|
28
|
+
UPDATE_USER_CURRENCY
|
|
29
|
+
} from '#graphql/commerce/mutations/updateUserCurrency.gql'
|
|
30
|
+
import {
|
|
31
|
+
getCurrencySymbol
|
|
32
|
+
} from '~/utils/currency'
|
|
33
|
+
import {
|
|
34
|
+
useCurrency
|
|
35
|
+
} from '~/app/composables/useCurrency'
|
|
36
|
+
|
|
37
|
+
const store = useCurrencyStore()
|
|
38
|
+
const selectedCurrency = ref('')
|
|
39
|
+
const availableCurrencies = ref([])
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
currentCurrency,
|
|
43
|
+
setCurrency
|
|
44
|
+
} = useCurrency()
|
|
45
|
+
|
|
46
|
+
// Fetch currency data from Magento
|
|
47
|
+
const {
|
|
48
|
+
result: currencyResult
|
|
49
|
+
} = useQuery(CURRENCY_QUERY)
|
|
50
|
+
|
|
51
|
+
// Mutation for updating user currency preference
|
|
52
|
+
const {
|
|
53
|
+
mutate: updateUserCurrency
|
|
54
|
+
} = useMutation(UPDATE_USER_CURRENCY)
|
|
55
|
+
|
|
56
|
+
// Watch for currency data changes
|
|
57
|
+
watch(currencyResult, (newResult) => {
|
|
58
|
+
if (newResult?.currency) {
|
|
59
|
+
const {
|
|
60
|
+
available_currency_codes,
|
|
61
|
+
exchange_rates,
|
|
62
|
+
default_display_currency_code
|
|
63
|
+
} = newResult.currency
|
|
64
|
+
|
|
65
|
+
// Map available currencies with their symbols
|
|
66
|
+
availableCurrencies.value = available_currency_codes.map(code => ({
|
|
67
|
+
code,
|
|
68
|
+
symbol: getCurrencySymbol(code),
|
|
69
|
+
rate: exchange_rates.find(rate => rate.currency_to === code)?.rate || 1
|
|
70
|
+
}))
|
|
71
|
+
|
|
72
|
+
// Set initial currency from user preference or default
|
|
73
|
+
const userCurrency = store.getCurrentCurrency
|
|
74
|
+
selectedCurrency.value = userCurrency || default_display_currency_code
|
|
75
|
+
|
|
76
|
+
// Update exchange rates in store
|
|
77
|
+
const ratesObj = {}
|
|
78
|
+
exchange_rates.forEach(({
|
|
79
|
+
currency_to,
|
|
80
|
+
rate
|
|
81
|
+
}) => {
|
|
82
|
+
ratesObj[currency_to] = rate
|
|
83
|
+
})
|
|
84
|
+
store.setExchangeRates(ratesObj)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Handle currency change
|
|
89
|
+
const handleCurrencyChange = async () => {
|
|
90
|
+
await setCurrency(selectedCurrency.value)
|
|
91
|
+
|
|
92
|
+
// If user is logged in, save preference
|
|
93
|
+
if (store.user) {
|
|
94
|
+
try {
|
|
95
|
+
await updateUserCurrency({
|
|
96
|
+
variables: {
|
|
97
|
+
currency: selectedCurrency.value
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('Failed to update user currency preference:', error)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onMounted(() => {
|
|
107
|
+
// Initialize with store currency if available
|
|
108
|
+
if (currentCurrency.value) {
|
|
109
|
+
selectedCurrency.value = currentCurrency.value
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<style scoped>
|
|
115
|
+
.currency-selector {
|
|
116
|
+
display: inline-block;
|
|
117
|
+
margin: 0 10px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.currency-select {
|
|
121
|
+
padding: 5px 10px;
|
|
122
|
+
border: 1px solid #ddd;
|
|
123
|
+
border-radius: 4px;
|
|
124
|
+
background-color: white;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
font-size: 14px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.currency-select:focus {
|
|
130
|
+
outline: none;
|
|
131
|
+
border-color: #007bff;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<section data-bs-version="5.1" data-sortbtn="btn-primary">
|
|
4
|
+
<div class="container">
|
|
5
|
+
<div class="row main justify-content-center">
|
|
6
|
+
<div class="col-lg-12 p-4">
|
|
7
|
+
<div class="image-element card-wrapper">
|
|
8
|
+
<NuxtImg class="brandLogo" :src="`${$directus.url}assets/${brand.image?.filename_disk}`" :alt="brand.name" />
|
|
9
|
+
<!--<div class="mbr-overlay card-overlay"></div>-->
|
|
10
|
+
<div class="wrapper">
|
|
11
|
+
<h5 class="card-title mbr-fonts-style align-left display-2">
|
|
12
|
+
{{ brand.name }}</h5>
|
|
13
|
+
<div class="collapsed-content">
|
|
14
|
+
<div class="mbr-section-btn"><a class="btn btn-md btn-info display-4"
|
|
15
|
+
:href="`/commerce/brand/${brand.id}`">View Brand</a></div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</section>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script setup>
|
|
27
|
+
import {
|
|
28
|
+
ref,
|
|
29
|
+
onMounted
|
|
30
|
+
} from 'vue'
|
|
31
|
+
|
|
32
|
+
const props = defineProps({
|
|
33
|
+
brand: {
|
|
34
|
+
type: Object,
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const {
|
|
39
|
+
brand
|
|
40
|
+
} = props;
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<!--<profilebar />-->
|
|
4
|
+
<section data-bs-version="5.1" class="firmm4_features1 features1 cid-uhBuptnWmV" id="features1-9v"
|
|
5
|
+
data-sortbtn="btn-primary">
|
|
6
|
+
<div class="container-fluid">
|
|
7
|
+
<div class="row">
|
|
8
|
+
<div class="col-12 title_block">
|
|
9
|
+
<h3 class="mbr-section-title mbr-fonts-style align-center mb-0 display-2">
|
|
10
|
+
<strong>Incentive: {{ incentive?.id }}</strong></h3>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="row justify-content-center">
|
|
14
|
+
<div class="card col-12">
|
|
15
|
+
<div class="card_wrapper">
|
|
16
|
+
<div class="card-box">
|
|
17
|
+
<div class="icon_block">
|
|
18
|
+
<div class="iconfont-wrapper">
|
|
19
|
+
<span class="mbr-iconfont mobi-mbri-cart-full mobi-mbri"></span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="card-text mbr-fonts-style display-4">Incentive: {{ incentive?.id }}</p>
|
|
23
|
+
<p class="card-text mbr-fonts-style display-4">Incentive Date: {{ new Date(incentive?.date_created).toLocaleDateString() }}</p>
|
|
24
|
+
<p class="card-text mbr-fonts-style display-4">Bill to Name: {{ incentive?.user_id?.first_name }} {{ incentive?.user_id?.last_name }}</p>
|
|
25
|
+
<p class="card-text mbr-fonts-style display-4">Status: {{ incentive?.status }}</p>
|
|
26
|
+
<p class="card-text mbr-fonts-style display-4">Type: {{ incentive?.incentive_type }}</p>
|
|
27
|
+
<p class="btn_link mbr-fonts-style display-4"><NuxtLink :to="`/incentive/${incentive?.id}`" class="text-secondary">View<span class="mobi-mbri mobi-mbri-right mbr-iconfont"></span></NuxtLink></p>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</section>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup>
|
|
38
|
+
const props = defineProps({
|
|
39
|
+
incentive: {
|
|
40
|
+
type: Object,
|
|
41
|
+
required: true,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
</script>
|