@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.
Files changed (206) hide show
  1. package/app/components/catalog/product/ProductAccordion/ProductAccordion.vue +41 -0
  2. package/app/components/catalog/product/ProductAccordion/__tests__/ProductAccordion.spec.ts +15 -0
  3. package/app/components/catalog/product/ProductAccordion/types.ts +5 -0
  4. package/app/components/catalog/product/ProductProperties/ProductProperties.vue +52 -0
  5. package/app/components/catalog/product/ProductProperties/__tests__/ProductProperties.spec.ts +15 -0
  6. package/app/components/catalog/product/ProductProperties/types.ts +5 -0
  7. package/app/components/catalog/product/ProductSlider/ProductSlider.vue +28 -0
  8. package/app/components/catalog/product/ProductSlider/__tests__/ProductSlider.spec.ts +14 -0
  9. package/app/components/catalog/product/ProductSlider/types.ts +7 -0
  10. package/app/components/catalog/product/RecommendedProducts/RecommendedProducts.vue +12 -0
  11. package/app/components/catalog/product/RecommendedProducts/types.ts +5 -0
  12. package/app/components/catalog/product/RenderContentProductSlider/RenderContentProductSlider.vue +11 -0
  13. package/app/components/catalog/product/add-attribute.vue +54 -0
  14. package/app/components/catalog/product/add-product-type.vue +54 -0
  15. package/app/components/catalog/product/add-product.vue +53 -0
  16. package/app/components/catalog/product/add-showcase.vue +52 -0
  17. package/app/components/catalog/product/add-station.vue +54 -0
  18. package/app/components/catalog/product/bestsellers.vue +69 -0
  19. package/app/components/catalog/product/bidding.vue +93 -0
  20. package/app/components/catalog/product/colorOptions.vue +58 -0
  21. package/app/components/catalog/product/deals.vue +61 -0
  22. package/app/components/catalog/product/exclusives.vue +58 -0
  23. package/app/components/catalog/product/featuredproducts.vue +69 -0
  24. package/app/components/catalog/product/giftCard.vue +63 -0
  25. package/app/components/catalog/product/latestproducts.vue +58 -0
  26. package/app/components/catalog/product/productCard.vue +71 -0
  27. package/app/components/catalog/product/productCompare.vue +60 -0
  28. package/app/components/catalog/product/productCompareTable.vue +441 -0
  29. package/app/components/catalog/product/productDetails.vue +120 -0
  30. package/app/components/catalog/product/productFaqs.vue +17 -0
  31. package/app/components/catalog/product/productGallery.vue +16 -0
  32. package/app/components/catalog/product/productQty.vue +54 -0
  33. package/app/components/catalog/product/productReviews.vue +56 -0
  34. package/app/components/catalog/product/productSpecs.vue +116 -0
  35. package/app/components/catalog/product/radiostation.vue +36 -0
  36. package/app/components/catalog/product/recentlyviewed.vue +43 -0
  37. package/app/components/catalog/product/relatedbrands.vue +54 -0
  38. package/app/components/catalog/product/relatedproducts.vue +58 -0
  39. package/app/components/catalog/product/relatedstations.vue +40 -0
  40. package/app/components/catalog/product/shippingOptions.vue +41 -0
  41. package/app/components/catalog/product/sizeOptions.vue +47 -0
  42. package/app/components/catalog/product/update-attribute-set.vue +209 -0
  43. package/app/components/catalog/product/update-attribute.vue +118 -0
  44. package/app/components/catalog/product/update-product.vue +372 -0
  45. package/app/components/catalog/product/update-showcase.vue +153 -0
  46. package/app/components/catalog/shops/relatedstores.vue +52 -0
  47. package/app/components/catalog/shops/restaurant.vue +66 -0
  48. package/app/components/catalog/shops/stores.vue +44 -0
  49. package/app/components/catalog/vendor/README.md +3 -0
  50. package/app/components/catalog/vendor/blocks/biggestcustomers.vue +33 -0
  51. package/app/components/catalog/vendor/blocks/lowestselling.vue +33 -0
  52. package/app/components/catalog/vendor/blocks/topcategories.vue +33 -0
  53. package/app/components/catalog/vendor/blocks/topproducts.vue +27 -0
  54. package/app/components/catalog/vendor/pages/attributes.vue +43 -0
  55. package/app/components/catalog/vendor/pages/commissions.vue +43 -0
  56. package/app/components/catalog/vendor/pages/crm.vue +67 -0
  57. package/app/components/catalog/vendor/pages/dashboard.vue +46 -0
  58. package/app/components/catalog/vendor/pages/emails.vue +43 -0
  59. package/app/components/catalog/vendor/pages/enquiries.vue +43 -0
  60. package/app/components/catalog/vendor/pages/invoices.vue +43 -0
  61. package/app/components/catalog/vendor/pages/orders.vue +68 -0
  62. package/app/components/catalog/vendor/pages/products.vue +55 -0
  63. package/app/components/catalog/vendor/pages/reviews.vue +48 -0
  64. package/app/components/catalog/vendor/pages/shipments.vue +43 -0
  65. package/app/components/catalog/vendor/pages/stores.vue +43 -0
  66. package/app/components/content/blocks/breadcrumbs.vue +0 -0
  67. package/app/components/content/blocks/currencySwitcher.vue +0 -0
  68. package/app/components/content/blocks/languageSwitcher.vue +0 -0
  69. package/app/components/content/blocks/videoproduct.vue +9 -0
  70. package/app/components/content/pages/checkout.vue +118 -0
  71. package/app/components/content/pages/meeoviGlobal.vue +68 -0
  72. package/app/components/content/pages/pickup-locations.vue +238 -0
  73. package/app/components/content/pages/showcases.vue +90 -0
  74. package/app/components/content/pages/success.vue +60 -0
  75. package/app/components/marketing/add-brand.vue +54 -0
  76. package/app/components/marketing/add-incentive.vue +54 -0
  77. package/app/components/marketing/promotions/giftcards.vue +127 -0
  78. package/app/components/marketing/promotions/subscriptions.vue +134 -0
  79. package/app/components/marketing/update-incentive.vue +326 -0
  80. package/app/components/menus/lowernav.vue +78 -0
  81. package/app/components/partials/LocaleSelector.vue +24 -0
  82. package/app/components/partials/ShoppingCart.vue +128 -0
  83. package/app/components/partials/StripePayment.vue +149 -0
  84. package/app/components/partials/addToCartBtn.vue +40 -0
  85. package/app/components/partials/cartItem.vue +124 -0
  86. package/app/components/partials/checkoutButton.vue +44 -0
  87. package/app/components/partials/compareBtn.vue +68 -0
  88. package/app/components/partials/ratings.vue +13 -0
  89. package/app/components/partials/store/CurrencySelector.vue +133 -0
  90. package/app/components/partials/store/StoreSwitcher.vue +13 -0
  91. package/app/components/related/brandCard.vue +41 -0
  92. package/app/components/related/incentiveCard.vue +44 -0
  93. package/app/components/related/invoiceCard.vue +43 -0
  94. package/app/components/related/orderCard.vue +43 -0
  95. package/app/components/related/relatedproducts.vue +17 -0
  96. package/app/components/sales/CartPageContent/CartPageContent.vue +43 -0
  97. package/app/components/sales/CheckoutAddress/CheckoutAddress.vue +50 -0
  98. package/app/components/sales/CheckoutAddress/__tests__/CheckoutAddress.spec.ts +16 -0
  99. package/app/components/sales/CheckoutAddress/types.ts +16 -0
  100. package/app/components/sales/CheckoutPayment/CheckoutPayment.vue +65 -0
  101. package/app/components/sales/CheckoutPayment/__tests__/CheckoutPayment.spec.ts +14 -0
  102. package/app/components/sales/CheckoutPayment/types.ts +12 -0
  103. package/app/components/sales/OrderSummary/OrderSummary.vue +57 -0
  104. package/app/components/sales/OrderSummary/__tests__/ContactInformation.spec.ts +52 -0
  105. package/app/components/sales/OrderSummary/types.ts +5 -0
  106. package/app/components/sales/incentives.vue +247 -0
  107. package/app/components/sales/invoices.vue +107 -0
  108. package/app/components/sales/orders.vue +378 -0
  109. package/app/components/sales/shipments.vue +65 -0
  110. package/app/components/sales/transactions.vue +109 -0
  111. package/app/components/shop/add-shop.vue +54 -0
  112. package/app/components/shop/cart/cartItem.vue +182 -0
  113. package/app/components/shop/cart/checkout.vue +415 -0
  114. package/app/components/shop/checkout/StripeCardElement.vue +206 -0
  115. package/app/components/shop/checkout/StripeCheckout.vue +49 -0
  116. package/app/components/shop/checkout/addressBilling.vue +263 -0
  117. package/app/components/shop/checkout/addressShipping.vue +175 -0
  118. package/app/components/shop/checkout/cart/ProductItem.vue +56 -0
  119. package/app/components/shop/checkout/cart/PromotionItem.vue +53 -0
  120. package/app/stores/cart.ts +1 -1
  121. package/app/types/Direction.type.ts +1 -1
  122. package/app/types/Global.type.ts +6 -6
  123. package/app/types/Layout.type.ts +1 -1
  124. package/app/{normalizers → types/normalizers}/Cart.query.ts +1 -1
  125. package/app/{normalizers → types/normalizers}/Cart.type.ts +2 -2
  126. package/app/{normalizers → types/normalizers}/Checkout.query.ts +2 -2
  127. package/app/{normalizers → types/normalizers}/Config.query.ts +1 -1
  128. package/app/{normalizers → types/normalizers}/Config.type.ts +1 -1
  129. package/app/{normalizers → types/normalizers}/ContactForm.query.ts +2 -2
  130. package/app/{normalizers → types/normalizers}/CreditMemo.type.ts +1 -1
  131. package/app/{normalizers → types/normalizers}/GiftCard.type.ts +1 -1
  132. package/app/{normalizers → types/normalizers}/Invoice.type.ts +1 -1
  133. package/app/{normalizers → types/normalizers}/MyAccount.query.ts +1 -1
  134. package/app/{normalizers → types/normalizers}/MyAccount.type.ts +1 -1
  135. package/app/{normalizers → types/normalizers}/NewsletterSubscription.query.ts +1 -1
  136. package/app/{normalizers → types/normalizers}/Order.query.ts +1 -1
  137. package/app/{normalizers → types/normalizers}/Order.type.ts +2 -2
  138. package/app/{normalizers → types/normalizers}/Payment.type.ts +1 -1
  139. package/app/{normalizers → types/normalizers}/ProductCompare.query.ts +1 -1
  140. package/app/{normalizers → types/normalizers}/ProductCompare.type.ts +1 -1
  141. package/app/{normalizers → types/normalizers}/ProductList.query.ts +2 -2
  142. package/app/{normalizers → types/normalizers}/ProductList.type.ts +2 -2
  143. package/app/{normalizers → types/normalizers}/Return.type.ts +1 -1
  144. package/app/{normalizers → types/normalizers}/Review.query.ts +1 -1
  145. package/app/{normalizers → types/normalizers}/Review.type.ts +1 -1
  146. package/app/{normalizers → types/normalizers}/StoreInPickUp.query.ts +1 -1
  147. package/app/{normalizers → types/normalizers}/Subscription.type.ts +1 -1
  148. package/app/{normalizers → types/normalizers}/Transaction.type.ts +1 -1
  149. package/app/{normalizers → types/normalizers}/UrlRewrites.query.ts +1 -1
  150. package/app/{normalizers → types/normalizers}/UrlRewrites.type.ts +1 -1
  151. package/app/{normalizers → types/normalizers}/Wishlist.query.ts +4 -4
  152. package/app/{normalizers → types/normalizers}/Wishlist.type.ts +1 -1
  153. package/app/utils/Address/Address.type.ts +1 -1
  154. package/app/utils/Address/index.ts +5 -5
  155. package/app/utils/Cart/Cart.ts +1 -1
  156. package/app/utils/Currency/Currency.ts +1 -1
  157. package/app/utils/History/History.type.ts +1 -1
  158. package/app/utils/Menu/Menu.ts +1 -1
  159. package/app/utils/Menu/Menu.type.ts +2 -2
  160. package/app/utils/Orders/Orders.ts +1 -1
  161. package/app/utils/Preload/CategoryPreload.ts +2 -2
  162. package/app/utils/Preload/ProductPreload.ts +1 -1
  163. package/app/utils/Preload/index.ts +1 -1
  164. package/app/utils/Price/Price.ts +1 -1
  165. package/app/utils/Product/Extract.ts +1 -1
  166. package/app/utils/Product/Product.ts +1 -1
  167. package/app/utils/Product/Product.type.ts +1 -1
  168. package/app/utils/Product/Transform.ts +1 -1
  169. package/app/utils/Wishlist/Wishlist.ts +1 -1
  170. package/app/utils/client.ts +19 -19
  171. package/package.json +1 -2
  172. package/tsconfig.json +2 -2
  173. package/app/cart/useCart.ts +0 -1
  174. /package/app/{components → composables}/ChevronIcon/ChevronIcon.config.ts +0 -0
  175. /package/app/{components → composables}/DateSelect/DateSelect.config.ts +0 -0
  176. /package/app/{components → composables}/Field/Field.config.ts +0 -0
  177. /package/app/{components → composables}/FieldDate/FieldDate.config.ts +0 -0
  178. /package/app/{components → composables}/Form/Form.type.ts +0 -0
  179. /package/app/{components → composables}/Product/Product.config.ts +0 -0
  180. /package/app/{components → composables}/Product/Stock.config.ts +0 -0
  181. /package/app/{components → composables}/ProductCustomizableOption/ProductCustomizableOption.config.ts +0 -0
  182. /package/app/{components → composables}/ProductGallery/ProductGallery.config.ts +0 -0
  183. /package/app/{components → composables}/ProductReviews/ProductReviews.config.ts +0 -0
  184. /package/app/{normalizers → types/normalizers}/Category.query.ts +0 -0
  185. /package/app/{normalizers → types/normalizers}/Category.type.ts +0 -0
  186. /package/app/{normalizers → types/normalizers}/CheckEmail.query.ts +0 -0
  187. /package/app/{normalizers → types/normalizers}/Checkout.type.ts +0 -0
  188. /package/app/{normalizers → types/normalizers}/CmsBlock.query.ts +0 -0
  189. /package/app/{normalizers → types/normalizers}/CmsBlock.type.ts +0 -0
  190. /package/app/{normalizers → types/normalizers}/CmsPage.query.ts +0 -0
  191. /package/app/{normalizers → types/normalizers}/CmsPage.type.ts +0 -0
  192. /package/app/{normalizers → types/normalizers}/Menu.query.ts +0 -0
  193. /package/app/{normalizers → types/normalizers}/Menu.type.ts +0 -0
  194. /package/app/{normalizers → types/normalizers}/ProductAlerts.query.ts +0 -0
  195. /package/app/{normalizers → types/normalizers}/Region.query.ts +0 -0
  196. /package/app/{normalizers → types/normalizers}/Region.type.ts +0 -0
  197. /package/app/{normalizers → types/normalizers}/Slider.query.ts +0 -0
  198. /package/app/{normalizers → types/normalizers}/Slider.type.ts +0 -0
  199. /package/app/{normalizers → types/normalizers}/StoreInPickUp.type.ts +0 -0
  200. /package/app/{routes → types/routes}/CategoryPage/CategoryPage.config.ts +0 -0
  201. /package/app/{routes → types/routes}/CategoryPage/CategoryPage.type.ts +0 -0
  202. /package/app/{routes → types/routes}/Checkout/Checkout.config.ts +0 -0
  203. /package/app/{routes → types/routes}/Checkout/Checkout.type.ts +0 -0
  204. /package/app/{routes → types/routes}/MyAccount/MyAccount.config.ts +0 -0
  205. /package/app/{routes → types/routes}/SearchPage/SearchPage.config.ts +0 -0
  206. /package/app/{routes → types/routes}/UrlRewrites/UrlRewrites.config.ts +0 -0
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <div data-testid="product-accordion">
3
+ <UiAccordionItem
4
+ summary-class="md:rounded-md w-full hover:bg-neutral-100 py-2 pl-4 pr-3 flex justify-between items-center"
5
+ v-model="productDetailsOpen"
6
+ >
7
+ <template #summary>
8
+ <h2 class="font-bold font-headings text-lg leading-6 md:text-2xl">
9
+ {{ $t('productDetails') }}
10
+ </h2>
11
+ </template>
12
+ <p>{{ product.description }}</p>
13
+ </UiAccordionItem>
14
+ <UiDivider class="my-4" />
15
+ <UiAccordionItem
16
+ summary-class="md:rounded-md w-full hover:bg-neutral-100 py-2 pl-4 pr-3 flex justify-between items-center"
17
+ >
18
+ <template #summary>
19
+ <h2 class="font-bold font-headings text-lg leading-6 md:text-2xl">
20
+ {{ $t('customerReviews') }}
21
+ </h2>
22
+ </template>
23
+ <UiReview v-for="review in productReviews" :key="review.id" :review="review" class="mb-4" />
24
+ </UiAccordionItem>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ import { useProductReviews } from '~/composables/useProductReviews';
30
+ import type { ProductAccordionPropsType } from './types';
31
+ import { toRefs, ref } from 'vue';
32
+
33
+ const props = defineProps<ProductAccordionPropsType>();
34
+
35
+ const { product } = toRefs(props);
36
+ const { data: productReviews, fetchProductReviews } = useProductReviews(product.value.slug);
37
+
38
+ const productDetailsOpen = ref(true);
39
+
40
+ fetchProductReviews(product.value.slug);
41
+ </script>
@@ -0,0 +1,15 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import ProductAccordion from '~/components/ProductAccordion/ProductAccordion.vue';
3
+ import { mockProduct } from '~/composables/useProductAttribute/__tests__/useProduct.mock';
4
+
5
+ describe('<ProductAccordion />', () => {
6
+ it('should render component', () => {
7
+ const { getByTestId } = mount(ProductAccordion, {
8
+ props: {
9
+ product: mockProduct,
10
+ },
11
+ });
12
+
13
+ expect(getByTestId('product-accordion'));
14
+ });
15
+ });
@@ -0,0 +1,5 @@
1
+ import { SfProduct } from "~/composables";
2
+
3
+ export type ProductAccordionPropsType = {
4
+ product: SfProduct;
5
+ };
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <div class="px-4" data-testid="product-properties">
3
+ <fieldset v-if="sizes?.length" class="pb-4">
4
+ <legend class="block mb-2 text-base font-medium leading-6 text-neutral-900">{{ $t('size') }}</legend>
5
+ <span v-for="{ label, value } in sizes" class="mr-2 mb-2 uppercase" :key="value">
6
+ <SfChip
7
+ class="min-w-[48px]"
8
+ size="sm"
9
+ :input-props="{
10
+ onClick: (e) => value == selectedSize && e.preventDefault(),
11
+ }"
12
+ :model-value="value === selectedSize"
13
+ @update:model-value="value !== selectedSize && setAttribute('size', value)"
14
+ >
15
+ {{ label }}
16
+ </SfChip>
17
+ </span>
18
+ </fieldset>
19
+ <fieldset v-if="sizes?.length" class="pb-2">
20
+ <legend class="block mb-2 text-base font-medium leading-6 text-neutral-900">{{ $t('color') }}</legend>
21
+ <span v-for="{ label, value } in colors" class="mr-2 mb-2 uppercase" :key="value">
22
+ <SfChip
23
+ class="min-w-[48px]"
24
+ size="sm"
25
+ :input-props="{
26
+ onClick: (e) => value == selectedSize && e.preventDefault(),
27
+ }"
28
+ :model-value="value === selectedColor"
29
+ @update:model-value="setAttribute('color', value)"
30
+ >
31
+ <template #prefix><SfThumbnail size="sm" :style="{ background: value }" /></template>
32
+ {{ label }}
33
+ </SfChip>
34
+ </span>
35
+ </fieldset>
36
+ </div>
37
+ </template>
38
+
39
+ <script setup lang="ts">
40
+ import { SfChip, SfThumbnail } from '@storefront-ui/vue';
41
+ import { ProductPropertiesProps } from './types';
42
+ import { computed } from 'vue';
43
+ import { useProductAttribute } from '~/composables/useProductAttribute';
44
+
45
+ const props = defineProps<ProductPropertiesProps>();
46
+
47
+ const { getAttributeList, getAttribute, setAttribute } = useProductAttribute(props.product, ['color', 'size']);
48
+ const sizes = getAttributeList('size');
49
+ const colors = getAttributeList('color');
50
+ const selectedSize = computed(() => getAttribute('size'));
51
+ const selectedColor = computed(() => getAttribute('color'));
52
+ </script>
@@ -0,0 +1,15 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import ProductProperties from '~/components/ProductProperties/ProductProperties.vue';
3
+ import { mockProduct } from '~/composables/useProductAttribute/__tests__/useProduct.mock';
4
+
5
+ describe('<ProductProperties />', () => {
6
+ it('should render component', () => {
7
+ const { getByTestId } = mount(ProductProperties, {
8
+ props: {
9
+ product: mockProduct
10
+ }
11
+ });
12
+
13
+ expect(getByTestId('product-properties'));
14
+ });
15
+ });
@@ -0,0 +1,5 @@
1
+ import { SfProduct } from "~/composables";
2
+
3
+ export type ProductPropertiesProps = {
4
+ product: SfProduct;
5
+ };
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <SfScrollable
3
+ buttons-placement="floating"
4
+ class="items-center pb-4"
5
+ :wrapper-class="wrapperClass"
6
+ data-testid="product-slider"
7
+ >
8
+ <UiProductCard
9
+ v-for="product in items"
10
+ :key="product.id"
11
+ class="max-w-[192px]"
12
+ :name="product.name ?? ''"
13
+ :slug="product.slug"
14
+ :image-url="product.primaryImage?.url ?? ''"
15
+ :image-alt="product.primaryImage?.alt ?? ''"
16
+ :price="product.price?.value.amount"
17
+ :rating-count="product.rating?.count"
18
+ :rating="product.rating?.average"
19
+ />
20
+ </SfScrollable>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { SfScrollable } from '@storefront-ui/vue';
25
+ import type { ProductSliderProps } from './types';
26
+
27
+ defineProps<ProductSliderProps>();
28
+ </script>
@@ -0,0 +1,14 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import ProductSlider from '~/components/ProductSlider/ProductSlider.vue';
3
+
4
+ describe('<ProductSlider />', () => {
5
+ it('should render component', () => {
6
+ const { getByTestId } = mount(ProductSlider, {
7
+ props: {
8
+ items: [],
9
+ },
10
+ });
11
+
12
+ expect(getByTestId('product-slider'));
13
+ });
14
+ });
@@ -0,0 +1,7 @@
1
+ import type { HTMLAttributes } from 'vue';
2
+ import { SfProduct } from '~/composables';
3
+
4
+ export type ProductSliderProps = {
5
+ items?: SfProduct[];
6
+ wrapperClass?: HTMLAttributes['class'];
7
+ };
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <p data-testid="recommended-products" class="my-4 typography-text-lg">
3
+ {{ $t('recommendedProducts') }}
4
+ </p>
5
+ <ProductSlider :items="products" />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { RecommendedProductsProps } from './types';
10
+
11
+ defineProps<RecommendedProductsProps>();
12
+ </script>
@@ -0,0 +1,5 @@
1
+ import { SfProduct } from "~/composables";
2
+
3
+ export type RecommendedProductsProps = {
4
+ products: SfProduct[];
5
+ };
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <NuxtLazyHydrate when-visible>
3
+ <ProductSlider v-bind="$attrs" :wrapper-class="['max-w-screen-3xl mx-auto px-4 md:px-10 mb-20', $attrs.class]" />
4
+ </NuxtLazyHydrate>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ inheritAttrs: false,
10
+ };
11
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <v-row justify="center">
3
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
4
+ <template v-slot:activator="{ props }">
5
+ <v-btn v-bind="props" class="rightAddBtn">
6
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Create a Attribute
7
+ </v-btn>
8
+ </template>
9
+ <v-card class="b-1">
10
+ <v-card-title>
11
+ <h3>Create New Attribute</h3>
12
+ </v-card-title>
13
+
14
+ <v-card-text>
15
+ <div v-if="formError" class="error">{{ formError }}</div>
16
+ <div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
17
+ <form @submit.prevent="submitForm">
18
+ <DirectusFormElement v-for="field in attributeFields" :key="field.field" :field="field" v-model="form[field.field]" />
19
+ <v-btn type="submit">Submit</v-btn>
20
+ </form>
21
+ </v-card-text>
22
+ </v-card>
23
+ </v-dialog>
24
+ </v-row>
25
+
26
+ </template>
27
+
28
+ <script setup>
29
+ import { ref } from 'vue'
30
+ import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
31
+ import { useDirectusForm } from '#shared/app/composables/globals/useDirectusForm'
32
+
33
+ const dialog = ref(false)
34
+ const { $directus, $readFieldsByCollection } = useNuxtApp()
35
+
36
+ const { data, error } = await useAsyncData('attributes', async () => {
37
+ return $directus.request($readFieldsByCollection('attributes'))
38
+ })
39
+
40
+ // guard against undefined/null data.value and empty arrays
41
+ if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
42
+ console.error(error)
43
+ throw createError({
44
+ statusCode: 404,
45
+ statusMessage: 'Attribute not found'
46
+ })
47
+ }
48
+
49
+ const attributeFields = data
50
+
51
+
52
+ // use composable for form handling (validation, submit, provide context)
53
+ const { form, formError, formSuccess, submitForm } = useDirectusForm('attributes', attributeFields, { clearOnSuccess: true, closeDialogRef: dialog })
54
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <v-row justify="center">
3
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
4
+ <template v-slot:activator="{ props }">
5
+ <v-btn v-bind="props" class="rightAddBtn">
6
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Create a Product Type
7
+ </v-btn>
8
+ </template>
9
+ <v-card class="b-1">
10
+ <v-card-title>
11
+ <h3>Create New Product Type</h3>
12
+ </v-card-title>
13
+
14
+ <v-card-text>
15
+ <div v-if="formError" class="error">{{ formError }}</div>
16
+ <div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
17
+ <form @submit.prevent="submitForm">
18
+ <DirectusFormElement v-for="field in productTypeFields" :key="field.field" :field="field" v-model="form[field.field]" />
19
+ <v-btn type="submit">Submit</v-btn>
20
+ </form>
21
+ </v-card-text>
22
+ </v-card>
23
+ </v-dialog>
24
+ </v-row>
25
+
26
+ </template>
27
+
28
+ <script setup>
29
+ import { ref } from 'vue'
30
+ import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
31
+ import { useDirectusForm } from '#shared/app/composables/globals/useDirectusForm'
32
+
33
+ const dialog = ref(false)
34
+ const { $directus, $readFieldsByCollection } = useNuxtApp()
35
+
36
+ const { data, error } = await useAsyncData('product_types', async () => {
37
+ return $directus.request($readFieldsByCollection('product_types'))
38
+ })
39
+
40
+ // guard against undefined/null data.value and empty arrays
41
+ if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
42
+ console.error(error)
43
+ throw createError({
44
+ statusCode: 404,
45
+ statusMessage: 'Product Type not found'
46
+ })
47
+ }
48
+
49
+ const productTypeFields = data
50
+
51
+
52
+ // use composable for form handling (validation, submit, provide context)
53
+ const { form, formError, formSuccess, submitForm } = useDirectusForm('product_types', productTypeFields, { clearOnSuccess: true, closeDialogRef: dialog })
54
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <v-row justify="center">
3
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
4
+ <template v-slot:activator="{ props }">
5
+ <v-btn v-bind="props" class="rightAddBtn">
6
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Create a Product
7
+ </v-btn>
8
+ </template>
9
+ <v-card class="b-1">
10
+ <v-card-title>
11
+ <h3>Create New Product</h3>
12
+ </v-card-title>
13
+
14
+ <v-card-text>
15
+ <div v-if="formError" class="error">{{ formError }}</div>
16
+ <div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
17
+ <form @submit.prevent="submitForm">
18
+ <DirectusFormElement v-for="field in productFields" :key="field.field" :field="field" v-model="form[field.field]" />
19
+ <v-btn type="submit">Submit</v-btn>
20
+ </form>
21
+ </v-card-text>
22
+ </v-card>
23
+ </v-dialog>
24
+ </v-row>
25
+ </template>
26
+
27
+ <script setup>
28
+ import { ref } from 'vue'
29
+ import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
30
+ import { useDirectusForm } from '#shared/app/composables/globals/useDirectusForm'
31
+
32
+ const dialog = ref(false)
33
+ const { $directus, $readFieldsByCollection } = useNuxtApp()
34
+
35
+ const { data, error } = await useAsyncData('products', async () => {
36
+ return $directus.request($readFieldsByCollection('products'))
37
+ })
38
+
39
+ // guard against undefined/null data.value and empty arrays
40
+ if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
41
+ console.error(error)
42
+ throw createError({
43
+ statusCode: 404,
44
+ statusMessage: 'Product not found'
45
+ })
46
+ }
47
+
48
+ const productFields = data
49
+
50
+
51
+ // use composable for form handling (validation, submit, provide context)
52
+ const { form, formError, formSuccess, submitForm } = useDirectusForm('products', productFields, { clearOnSuccess: true, closeDialogRef: dialog })
53
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <v-row justify="center">
3
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
4
+ <template v-slot:activator="{ props }">
5
+ <v-btn v-bind="props" class="rightAddBtn">
6
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Create a Showcase
7
+ </v-btn>
8
+ </template>
9
+ <v-card class="b-1">
10
+ <v-card-title>
11
+ <h3>Create New Showcase</h3>
12
+ </v-card-title>
13
+
14
+ <v-card-text>
15
+ <div v-if="formError" class="error">{{ formError }}</div>
16
+ <div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
17
+ <form @submit.prevent="submitForm">
18
+ <DirectusFormElement v-for="field in showcaseFields" :key="field.field" :field="field" v-model="form[field.field]" />
19
+ <v-btn type="submit">Submit</v-btn>
20
+ </form>
21
+ </v-card-text>
22
+ </v-card>
23
+ </v-dialog>
24
+ </v-row>
25
+ </template>
26
+
27
+ <script setup>
28
+ import { ref } from 'vue'
29
+ import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
30
+ import { useDirectusForm } from '#shared/app/composables/globals/useDirectusForm'
31
+
32
+ const dialog = ref(false)
33
+ const { $directus, $readFieldsByCollection } = useNuxtApp()
34
+
35
+ const { data, error } = await useAsyncData('showcases', async () => {
36
+ return $directus.request($readFieldsByCollection('showcases'))
37
+ })
38
+
39
+ // guard against undefined/null data.value and empty arrays
40
+ if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
41
+ console.error(error)
42
+ throw createError({
43
+ statusCode: 404,
44
+ statusMessage: 'Showcase not found'
45
+ })
46
+ }
47
+
48
+ const showcaseFields = data
49
+
50
+ // use composable for form handling (validation, submit, provide context)
51
+ const { form, formError, formSuccess, submitForm } = useDirectusForm('showcases', showcaseFields, { clearOnSuccess: true, closeDialogRef: dialog })
52
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <v-row justify="center">
3
+ <v-dialog v-model="dialog" :scrim="false" transition="dialog-bottom-transition">
4
+ <template v-slot:activator="{ props }">
5
+ <v-btn v-bind="props" class="rightAddBtn">
6
+ <v-icon start icon="fas:fa fa-plus"></v-icon>Create a Station
7
+ </v-btn>
8
+ </template>
9
+ <v-card class="b-1">
10
+ <v-card-title>
11
+ <h3>Create New Station</h3>
12
+ </v-card-title>
13
+
14
+ <v-card-text>
15
+ <div v-if="formError" class="error">{{ formError }}</div>
16
+ <div v-else-if="formSuccess" class="success">{{ formSuccess }}</div>
17
+ <form @submit.prevent="submitForm">
18
+ <DirectusFormElement v-for="field in stationFields" :key="field.field" :field="field" v-model="form[field.field]" />
19
+ <v-btn type="submit">Submit</v-btn>
20
+ </form>
21
+ </v-card-text>
22
+ </v-card>
23
+ </v-dialog>
24
+ </v-row>
25
+
26
+ </template>
27
+
28
+ <script setup>
29
+ import { ref } from 'vue'
30
+ import DirectusFormElement from '#shared/app/components/ui/forms/DirectusFormElement.vue'
31
+ import { useDirectusForm } from '#shared/app/composables/globals/useDirectusForm'
32
+
33
+ const dialog = ref(false)
34
+ const { $directus, $readFieldsByCollection } = useNuxtApp()
35
+
36
+ const { data, error } = await useAsyncData('radios', async () => {
37
+ return $directus.request($readFieldsByCollection('radios'))
38
+ })
39
+
40
+ // guard against undefined/null data.value and empty arrays
41
+ if (error.value || data.value == null || (data.value?.length ?? 0) === 0) {
42
+ console.error(error)
43
+ throw createError({
44
+ statusCode: 404,
45
+ statusMessage: 'Station not found'
46
+ })
47
+ }
48
+
49
+ const stationFields = data
50
+
51
+
52
+ // use composable for form handling (validation, submit, provide context)
53
+ const { form, formError, formSuccess, submitForm } = useDirectusForm('radios', stationFields, { clearOnSuccess: true, closeDialogRef: dialog })
54
+ </script>
@@ -0,0 +1,69 @@
1
+ <template>
2
+ <div>
3
+ <section data-bs-version="5.1" class="mbr-section features20 cid-txNnCwzel4" id="features20-4t"
4
+ data-sortbtn="btn-primary">
5
+ <div class="container-fluid">
6
+ <h2 class="mbr-section-title align-left mbr-fonts-style display-5">
7
+ Best Sellers</h2>
8
+ <div class="underline align-left pb-3">
9
+ <div class="line"></div>
10
+ </div>
11
+ <v-sheet class="mx-auto">
12
+ <v-slide-group v-model="model" class="pa-4" show-arrows>
13
+ <v-slide-group-item v-slot="{ isSelected, toggle, selectedClass }"
14
+ v-for="(products, index) in bestsellers" :key="index">
15
+ <productCard :product="products" :class="['ma-4', selectedClass]" @click="toggle" />
16
+
17
+ <div class="d-flex fill-height align-center justify-center">
18
+ <v-scale-transition>
19
+ <v-icon v-if="isSelected" color="white" icon="mdi-close-circle-outline" size="48"></v-icon>
20
+ </v-scale-transition>
21
+ </div>
22
+ </v-slide-group-item>
23
+ </v-slide-group>
24
+ </v-sheet>
25
+ </div>
26
+ </section>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup>
31
+ import productCard from './productCard.vue'
32
+
33
+ const model = ref(null);
34
+ const {
35
+ $directus,
36
+ $readItems
37
+ } = useNuxtApp()
38
+
39
+ const {
40
+ data: bestsellers
41
+ } = await useAsyncData('bestsellers', () => {
42
+ return $directus.request($readItems('products', {
43
+ fields: ['*',
44
+ 'products.products_id.*',
45
+ 'products.products_id.image.*',
46
+ 'currency.currency_id.*',
47
+ 'brands.brands_id.*',
48
+ 'image.*',
49
+ ],
50
+ limit: 10,
51
+ filter: {
52
+ lists: {
53
+ lists_id: {
54
+ lists_types: {
55
+ lists_types_id: {
56
+ name: {
57
+ _eq: "Best Sellers"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ },
63
+ status: {
64
+ _eq: "published"
65
+ }
66
+ }
67
+ }))
68
+ })
69
+ </script>
@@ -0,0 +1,93 @@
1
+ <template>
2
+ <div class="auction-item">
3
+ <h2>{{ item.name }}</h2>
4
+ <NuxtImg loading="lazy" :src="item.image" :alt="item.name" />
5
+ <p>Current Bid: ${{ currentBid }}</p>
6
+ <p>Time Left: {{ timeLeft }}</p>
7
+ <input v-model.number="bidAmount" type="number" :min="minBid" step="0.01" />
8
+ <button @click="placeBid" :disabled="!canBid">Place Bid</button>
9
+ <div v-if="bidHistory.length > 0">
10
+ <h3>Bid History</h3>
11
+ <ul>
12
+ <li v-for="bid in bidHistory" :key="bid.id">
13
+ {{ bid.user }}: ${{ bid.amount }}
14
+ </li>
15
+ </ul>
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script setup>
21
+ import {
22
+ ref,
23
+ computed,
24
+ onMounted,
25
+ onUnmounted
26
+ } from 'vue'
27
+ import {
28
+ useUserStore
29
+ } from '#auth/app/stores/user'
30
+
31
+ const {
32
+ $io
33
+ } = useNuxtApp()
34
+ const userStore = useUserStore()
35
+
36
+ const props = defineProps({
37
+ itemId: {
38
+ type: String,
39
+ required: true
40
+ }
41
+ })
42
+
43
+ const item = ref({})
44
+ const currentBid = ref(0)
45
+ const timeLeft = ref('')
46
+ const bidAmount = ref(0)
47
+ const bidHistory = ref([])
48
+
49
+ const minBid = computed(() => currentBid.value + 1)
50
+ const canBid = computed(() => bidAmount.value >= minBid.value)
51
+
52
+ onMounted(async () => {
53
+ // Fetch initial item data
54
+ const {
55
+ data
56
+ } = await useFetch(`/api/items/${props.itemId}`)
57
+ item.value = data.value
58
+ currentBid.value = item.value.startingBid
59
+
60
+ $io.emit('join-auction', props.itemId)
61
+
62
+ $io.on('new-bid', (bid) => {
63
+ currentBid.value = bid.amount
64
+ bidHistory.value.unshift(bid)
65
+ })
66
+
67
+ $io.on('auction-time-update', (time) => {
68
+ timeLeft.value = time
69
+ })
70
+
71
+ $io.on('auction-ended', (winner) => {
72
+ alert(`Auction ended! Winner: ${winner}`)
73
+ })
74
+ })
75
+
76
+ const placeBid = () => {
77
+ if (canBid.value) {
78
+ $io.emit('place-bid', {
79
+ itemId: props.itemId,
80
+ amount: bidAmount.value,
81
+ user: userStore.currentUser.name
82
+ })
83
+ bidAmount.value = 0
84
+ }
85
+ }
86
+
87
+ onUnmounted(() => {
88
+ $io.off('new-bid')
89
+ $io.off('auction-time-update')
90
+ $io.off('auction-ended')
91
+ $io.emit('leave-auction', props.itemId)
92
+ })
93
+ </script>