Compare commits
3 Commits
7b0d9b9c7b
...
7d0a449a1e
Author | SHA1 | Date | |
---|---|---|---|
7d0a449a1e | |||
77a490a94d | |||
35575eda6f |
12
components/CartPopup.vue
Normal file
12
components/CartPopup.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-[50px] bg-bg-light dark:bg-bg-dark border border-button rounded-2xl">
|
||||||
|
<div class="" v-if="productStore.productList">
|
||||||
|
{{ productStore.productList }}
|
||||||
|
</div>
|
||||||
|
<UiButtonArrow class="w-full" type="fill" :arrow="true">Přejít k pokladně</UiButtonArrow>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const productStore = useProductStore()
|
||||||
|
</script>
|
@ -2,7 +2,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<!-- xl -->
|
<!-- xl -->
|
||||||
<div class="w-full border-b border-border">
|
<div class="w-full border-b border-border">
|
||||||
<UiContainer>
|
<UiContainer class="relative">
|
||||||
<div class="hidden h-[120px] w-full items-center gap-[145px] xl:flex">
|
<div class="hidden h-[120px] w-full items-center gap-[145px] xl:flex">
|
||||||
<ul class="flex items-center justify-between whitespace-nowrap w-full">
|
<ul class="flex items-center justify-between whitespace-nowrap w-full">
|
||||||
<li v-for="(item, index) in menuStore.menu" @click="menuStore.navigateToItem(item)" :key="index"
|
<li v-for="(item, index) in menuStore.menu" @click="menuStore.navigateToItem(item)" :key="index"
|
||||||
@ -18,18 +18,21 @@
|
|||||||
<div class="w-full flex items-center justify-between">
|
<div class="w-full flex items-center justify-between">
|
||||||
<div class="flex items-center gap-[30px]">
|
<div class="flex items-center gap-[30px]">
|
||||||
<i class="uil uil-user text-[31px] cursor-pointer"></i>
|
<i class="uil uil-user text-[31px] cursor-pointer"></i>
|
||||||
<i class="uil uil-shopping-cart text-[31px] cursor-pointer"></i>
|
<i @click="openCart = !openCart" class="uil uil-shopping-cart text-[31px] cursor-pointer"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<LangSwitcher />
|
<LangSwitcher />
|
||||||
<CountryCurrencySelector />
|
<CountryCurrencySelector />
|
||||||
</div>
|
</div>
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
<button
|
<button @click="menuStore.navigateToShop"
|
||||||
class="hover:bg-button-hover bg-button cursor-pointer rounded-xl px-6 py-3 font-medium text-white transition-all text-inter">
|
class="hover:bg-button-hover bg-button cursor-pointer rounded-xl px-6 py-3 font-medium text-white transition-all text-inter">
|
||||||
E-shop
|
{{ $t('eshop') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="openCart" class="max-w-[1067px] absolute top-[130px] z-50 right-20">
|
||||||
|
<CartPopup />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UiContainer>
|
</UiContainer>
|
||||||
</div>
|
</div>
|
||||||
@ -209,15 +212,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import CartPopup from "./CartPopup.vue";
|
||||||
import CountryCurrencySelector from "./CountryCurrencySelector.vue";
|
import CountryCurrencySelector from "./CountryCurrencySelector.vue";
|
||||||
import LangSwitcher from "./LangSwitcher.vue";
|
import LangSwitcher from "./LangSwitcher.vue";
|
||||||
|
|
||||||
const menuStore = useMenuStore();
|
const menuStore = useMenuStore();
|
||||||
|
const productStore = useProductStore();
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
|
const openCart = ref(false);
|
||||||
const colorMode = useColorMode();
|
const colorMode = useColorMode();
|
||||||
|
|
||||||
const route = useRoute()
|
productStore.getCart()
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
const isDark = computed({
|
const isDark = computed({
|
||||||
get() {
|
get() {
|
||||||
return colorMode.value === "dark";
|
return colorMode.value === "dark";
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<UiButtonArrow :arrow="true" class="mx-auto" type="fill">E-shop</UiButtonArrow>
|
<UiButtonArrow :arrow="true" class="mx-auto" type="fill">{{ $t('eshop') }}</UiButtonArrow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- calculator-block -->
|
<!-- calculator-block -->
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
}" />
|
}" />
|
||||||
<div class="w-full sm:w-[80%] mx-auto my-auto xl:w-full xl:px-12 ">
|
<div class="w-full sm:w-[80%] mx-auto my-auto xl:w-full xl:px-12 ">
|
||||||
<div class="space-25-55">
|
<div class="space-25-55">
|
||||||
<div class="flex justify-between">
|
<div class="flex flex-wrap-reverse gap-y-4 justify-between">
|
||||||
<h2 class="h2-bold-bounded">{{ $t('login') }}</h2>
|
<h2 class="h2-bold-bounded">{{ $t('login') }}</h2>
|
||||||
<button
|
<button
|
||||||
class="h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer">{{
|
class="h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer">{{
|
||||||
@ -15,28 +15,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('email') }}</p>
|
<p class="pl-6">{{ $t('email') }}</p>
|
||||||
<input :placeholder="$t('email')" type="text"
|
<input v-model="store.email" :placeholder="$t('email')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('password') }}</p>
|
<p class="pl-6">{{ $t('password') }}</p>
|
||||||
<input :placeholder="$t('placeholder_password')" type="text"
|
<input v-model="store.password" :placeholder="$t('placeholder_password')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-button hover:text-button-hover transition-all font-medium mt-[30px] cursor-pointer">{{
|
<p class="text-button hover:text-button-hover transition-all font-medium mt-[30px] cursor-pointer">{{
|
||||||
$t('forgot_password_question')
|
$t('forgot_password_question')
|
||||||
}}</p>
|
}}</p>
|
||||||
<div class="py-[25px] sm:py-12 border-b border-gray flex justify-center w-full">
|
<div class="py-[25px] sm:py-12 border-b border-gray flex justify-center w-full">
|
||||||
<UiButtonArrow type="fill" :arrow="true">{{ $t('login') }}</UiButtonArrow>
|
<UiButtonArrow @click="store.logIn()" type="fill" :arrow="true">{{ $t('login') }}</UiButtonArrow>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[25px] sm:mt-[30px] w-full flex justify-center gap-3">
|
<div class="mt-[25px] sm:mt-[30px] w-full flex justify-center gap-3">
|
||||||
<p class="cursor-pointer hover:underline transition-all">{{
|
<p class="cursor-pointer hover:underline transition-all">{{
|
||||||
$t('no_account')
|
$t('no_account')
|
||||||
}}</p>
|
}}</p>
|
||||||
<p class="text-button cursor-pointer hover:text-button-hover">{{
|
<p class="text-button cursor-pointer hover:text-button-hover">{{
|
||||||
$t('sign_up_now')
|
$t('sign_up_now')
|
||||||
}}</p>
|
}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</UiContainer>
|
</UiContainer>
|
||||||
@ -49,4 +49,6 @@ type Component = {
|
|||||||
section_id: string;
|
section_id: string;
|
||||||
section_img: string;
|
section_img: string;
|
||||||
section_lang_data: {};
|
section_lang_data: {};
|
||||||
};</script>
|
};
|
||||||
|
const store = useStore()
|
||||||
|
</script>
|
@ -1,27 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiContainer class="space-y-[40px] sm:space-y-[55px] md:space-y-[75px]">
|
<UiContainer class="space-y-[40px] sm:space-y-[55px] md:space-y-[75px]">
|
||||||
<div
|
<div :class="[
|
||||||
:class="[
|
'sm:mx-[50px] md:mx-0 xl:mx-[92px] flex items-stretch',
|
||||||
'sm:mx-[50px] md:mx-0 xl:mx-[92px] flex items-stretch',
|
itemCount === 1 ? 'justify-center' : 'justify-between gap-2',
|
||||||
itemCount === 1 ? 'justify-center' : 'justify-between gap-2',
|
]">
|
||||||
]"
|
|
||||||
>
|
|
||||||
<!-- product -->
|
<!-- product -->
|
||||||
<div
|
<div v-for="(item, index) in productStore.productList" :key="index"
|
||||||
v-for="(item, index) in productStore.productList"
|
class="w-[200px] sm:w-[260px] md:w-[290px] sm:py-5 sm:px-[15px] py-[15px] px-[10px] bg-block rounded-2xl flex flex-col items-center gap-5 sm:gap-7">
|
||||||
:key="index"
|
<img :src="`https://www.yourgold.cz/api/public/file/${item.cover_picture_uuid}.webp`" alt="pics"
|
||||||
class="w-[200px] sm:w-[260px] md:w-[290px] sm:py-5 sm:px-[15px] py-[15px] px-[10px] bg-block rounded-2xl flex flex-col items-center gap-5 sm:gap-7"
|
class="max-h-[150px] sm:max-h-[180px] md:max-h-[205px]" />
|
||||||
>
|
|
||||||
<img
|
|
||||||
:src="`https://www.yourgold.cz/api/public/file/${item.cover_picture_uuid}.webp`"
|
|
||||||
alt="pics"
|
|
||||||
class="max-h-[150px] sm:max-h-[180px] md:max-h-[205px]"
|
|
||||||
/>
|
|
||||||
<div class="flex flex-col justify-between h-full">
|
<div class="flex flex-col justify-between h-full">
|
||||||
<div class="flex flex-col gap-[10px] sm:gap-[15px] w-full">
|
<div class="flex flex-col gap-[10px] sm:gap-[15px] w-full">
|
||||||
<h3
|
<h3 class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark">
|
||||||
class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark"
|
|
||||||
>
|
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
|
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
|
||||||
@ -33,24 +23,19 @@
|
|||||||
{{ item.formatted_price }}
|
{{ item.formatted_price }}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
class="w-9 h-9 md:w-12 md:h-12 rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center"
|
class="w-9 h-9 md:w-12 md:h-12 rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center">
|
||||||
>
|
<i class="uil uil-shopping-cart text-[25px] md:text-[24px] text-bg-light"></i>
|
||||||
<i
|
|
||||||
class="uil uil-shopping-cart text-[25px] md:text-[24px] text-bg-light"
|
|
||||||
></i>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-6 md:flex-row items-center justify-between">
|
<div class="flex flex-col gap-6 md:flex-row items-center justify-between">
|
||||||
<h3
|
<h3 class="h4-uppercase-bold-inter w-full text-center md:text-start xl:max-w-[50%]">
|
||||||
class="h4-uppercase-bold-inter w-full text-center md:text-start xl:max-w-[50%]"
|
|
||||||
>
|
|
||||||
Zlato je jistota, která nepodléhá času. Udělejte dnes rozhodnutí, které
|
Zlato je jistota, která nepodléhá času. Udělejte dnes rozhodnutí, které
|
||||||
vás ochrání zítra
|
vás ochrání zítra
|
||||||
</h3>
|
</h3>
|
||||||
<UiButtonArrow type="fill" :arrow="true">E-shop</UiButtonArrow>
|
<UiButtonArrow @click="menuStore.navigateToShop" type="fill" :arrow="true">{{ $t('eshop') }}</UiButtonArrow>
|
||||||
</div>
|
</div>
|
||||||
</UiContainer>
|
</UiContainer>
|
||||||
</template>
|
</template>
|
||||||
@ -72,6 +57,7 @@ type Component = {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const menuStore = useMenuStore()
|
||||||
const itemCount = ref(4);
|
const itemCount = ref(4);
|
||||||
const productStore = useProductStore();
|
const productStore = useProductStore();
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<UiContainer class="flex py-20 sm:py-14">
|
<UiContainer class="flex py-[15px] xl:py-20 sm:py-0">
|
||||||
<div class="hidden xl:block rounded-2xl min-w-[40%] h-[830px]" :style="{
|
<div class="hidden xl:block rounded-2xl min-w-[40%] h-[830px]" :style="{
|
||||||
backgroundImage: `url('/api/files/${component.image_collection}/${component.section_id}/${component.section_img[0]}?thumb=1200x0')`,
|
backgroundImage: `url('/api/files/${component.image_collection}/${component.section_id}/${component.section_img[0]}?thumb=1200x0')`,
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
@ -13,62 +13,96 @@
|
|||||||
class="h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer">{{
|
class="h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer">{{
|
||||||
$t('back_to_home') }}</button>
|
$t('back_to_home') }}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[30px]">
|
<div class="space-y-[25px] sm:space-y-[30px]">
|
||||||
<p>Obecné informace</p>
|
<p>Obecné informace</p>
|
||||||
<div class="grid grid-cols-2 gap-[30px]">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-[30px]">
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('first_name') }}</p>
|
<p class="pl-6">{{ $t('first_name') }}</p>
|
||||||
<input :placeholder="$t('first_name')" type="text"
|
<input :placeholder="$t('first_name')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="text-sm sm:text-xl border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('last_name') }}</p>
|
<p class="pl-6">{{ $t('last_name') }}</p>
|
||||||
<input :placeholder="$t('last_name')" type="text"
|
<input :placeholder="$t('last_name')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="text-sm sm:text-xl border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('email') }}</p>
|
<p class="pl-6">{{ $t('email') }}</p>
|
||||||
<input :placeholder="$t('email')" type="text"
|
<input :placeholder="$t('email')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="text-sm sm:text-xl border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]" ref="dropdownRef">
|
||||||
<p class="pl-6">{{ $t('phone') }}</p>
|
<p class="pl-6">{{ $t('phone') }}</p>
|
||||||
<div class="flex items-center border-2 border-block rounded-lg">
|
<div class="flex items-center border-2 border-block rounded-lg">
|
||||||
<div
|
<div
|
||||||
class="relative z-50 bg-inherit ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 space-y-1">
|
class="relative z-50 bg-inherit ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0">
|
||||||
<div class="px-[25px]" @click="dropCountry = !dropCountry">
|
<div class="px-[25px]" @click="dropCountry = !dropCountry">
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
|
class="flex items-center gap-2 text-sm sm:text-xl uppercase text-text-light dark:text-text-dark">
|
||||||
{{ menuStore.selectedPhoneCountry.name }} <span> <i
|
<span :class="[dropCountry && 'rotate-180', 'transition-all']"> <i
|
||||||
class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
|
class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
|
||||||
|
<p class="text-sm sm:text-xl">
|
||||||
|
{{ menuStore.selectedPhoneCountry.iso_code }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="dropCountry"
|
<div v-if="dropCountry"
|
||||||
class="absolute bg-bg-light rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 ring-0 cursor-pointer w-full focus:ring-0 outline-none focus-visible:ring-0 border border-button py-[10px] px-[5px]">
|
class="mt-2 absolute bg-bg-light dark:bg-bg-dark rounded-[5px] ring-0 cursor-pointer w-full border border-button py-[10px] px-[5px] overflow-hidden">
|
||||||
<div class="overflow-auto h-[200px] w-full">
|
<div class="overflow-y-auto h-[200px] w-full">
|
||||||
<p @click="() => {
|
<p v-for="item in menuStore.countryList" @click="() => {
|
||||||
menuStore.selectedPhoneCountry = item
|
menuStore.selectedPhoneCountry = item
|
||||||
dropCountry = false
|
dropCountry = false
|
||||||
}" class="w-full hover:bg-block dark:hover:bg-button pl-2 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
|
}" class="w-full truncate whitespace-nowrap overflow-hidden hover:bg-block dark:hover:bg-button pl-2 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
|
||||||
v-for="item in menuStore.countryList">
|
:title="item.name">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{{ menuStore.selectedPhoneCountry.call_prefix }}
|
<p class="text-sm sm:text-xl font-normal">{{ menuStore.selectedPhoneCountry.call_prefix
|
||||||
|
}}</p>
|
||||||
<input :placeholder="$t('phone')" type="text"
|
<input :placeholder="$t('phone')" type="text"
|
||||||
class="placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0" />
|
class="text-sm sm:text-xl placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('email') }}</p>
|
<p class="pl-6">{{ $t('account_type') }}</p>
|
||||||
<input :placeholder="$t('email')" type="text"
|
<USelect v-model="selectedType" :items="component.section_lang_data.account_types"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
value-key="name" :searchable="false" :ui="{
|
||||||
|
base: 'bg-inherit ring-0 cursor-pointer w-auto focus:ring-0 outline-none focus-visible:ring-0 h-[50px] sm:h-[67px] w-full p-0',
|
||||||
|
trailing: 'hidden w-full',
|
||||||
|
viewport: 'ring-0 min-w-full',
|
||||||
|
content: 'bg-bg-light dark:bg-bg-dark ring-0 border border-button',
|
||||||
|
leading:
|
||||||
|
'left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 p-0 w-full',
|
||||||
|
group: 'px-[5px] py-[10px]',
|
||||||
|
item: 'hover:bg-block dark:hover:bg-button rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 min-w-full',
|
||||||
|
}">
|
||||||
|
<template #leading="{ modelValue }">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between gap-2 uppercase text-sm sm:text-xl border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 w-full h-[50px] sm:h-[67px]">
|
||||||
|
<p class="truncate whitespace-nowrap">
|
||||||
|
{{component.section_lang_data.account_types.find((item) => item.name ===
|
||||||
|
modelValue)?.name}}</p>
|
||||||
|
<span> <i
|
||||||
|
class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #item="{ item }">
|
||||||
|
<div class="flex items-center gap-2 cursor-pointer min-w-full">
|
||||||
|
<p
|
||||||
|
class="truncate whitespace-nowrap text-sm sm:text-xl font-medium uppercase text-text-light dark:text-text-dark opacity-100">
|
||||||
|
{{ item.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</USelect>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-[15px]">
|
<div class="space-y-[15px]">
|
||||||
<p class="pl-6">{{ $t('password') }}</p>
|
<p class="pl-6">{{ $t('partner_code') }}</p>
|
||||||
<input :placeholder="$t('placeholder_password')" type="text"
|
<input :placeholder="$t('placeholder_password')" type="text"
|
||||||
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
class="border-2 border-block placeholder:text-gray dark:placeholder:text-button-disabled text-bg-dark dark:text-bg-light rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
|
||||||
</div>
|
</div>
|
||||||
@ -91,13 +125,26 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineProps<{ component: Component }>();
|
import { onClickOutside } from "@vueuse/core";
|
||||||
|
const props = defineProps<{ component: Component }>();
|
||||||
type Component = {
|
type Component = {
|
||||||
image_collection: string;
|
image_collection: string;
|
||||||
section_id: string;
|
section_id: string;
|
||||||
section_img: string;
|
section_img: string;
|
||||||
section_lang_data: {};
|
section_lang_data: {
|
||||||
|
account_types: [{
|
||||||
|
id: number,
|
||||||
|
name: string
|
||||||
|
}]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
const menuStore = useMenuStore()
|
const menuStore = useMenuStore()
|
||||||
|
const dropdownRef = ref(null);
|
||||||
const dropCountry = ref()
|
const dropCountry = ref()
|
||||||
|
|
||||||
|
const selectedType = ref(props.component.section_lang_data.account_types[0].name)
|
||||||
|
|
||||||
|
onClickOutside(dropdownRef, () => {
|
||||||
|
dropCountry.value = false
|
||||||
|
});
|
||||||
</script>
|
</script>
|
46
components/section/shop-page/CategoryTree.vue
Normal file
46
components/section/shop-page/CategoryTree.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="hasMainCategory" :class="['flex flex-col gap-[25px]', isOpen && 'border-b border-block pb-[10px]']">
|
||||||
|
<div @click="toggle" class="flex items-center justify-between rounded-lg cursor-pointer">
|
||||||
|
<div class="flex items-center justify-between w-full">
|
||||||
|
<p class="text-lg xl:text-2xl font-extrabold text-black dark:text-white">
|
||||||
|
{{ mainCategoryName }}
|
||||||
|
</p>
|
||||||
|
<span :class="['flex items-center justify-center',isOpen && 'rotate-180', 'transition-all']"><i
|
||||||
|
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="flex flex-col gap-[25px]" v-show="isOpen">
|
||||||
|
<li @click="$emit('change-category', child)" v-for="child in subcategories" :key="child.id"
|
||||||
|
class="text-base xl:text-lg cursor-pointer flex justify-between items-center"
|
||||||
|
:class="child.id === props.active ? 'text-yellow' : ''">
|
||||||
|
<span>{{ child.langs[0].Name }}</span>
|
||||||
|
<span>12</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<!-- Optional: Placeholder or message when main category is not available -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
// Define the props
|
||||||
|
const props = defineProps({
|
||||||
|
data: Array,
|
||||||
|
active: Number
|
||||||
|
});
|
||||||
|
|
||||||
|
const isOpen = ref(false);
|
||||||
|
|
||||||
|
const toggle = () => {
|
||||||
|
isOpen.value = !isOpen.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Computed properties
|
||||||
|
const hasMainCategory = computed(() => props.data.length > 0 && props.data[0].langs && props.data[0].langs.length > 0);
|
||||||
|
const mainCategoryName = computed(() => hasMainCategory.value ? props.data[0].langs[0].Name : '');
|
||||||
|
const subcategories = computed(() => hasMainCategory.value && props.data[0].children ? props.data[0].children : []);
|
||||||
|
</script>
|
81
components/section/shop-page/CurrencyRatesBar.vue
Normal file
81
components/section/shop-page/CurrencyRatesBar.vue
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex gap-24">
|
||||||
|
<div class="price-container">
|
||||||
|
<div class="slider" v-html="duplicatedContent">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
let res = "<span class=\"text-base sm:text-lg font-bold\">CZK price (to EUR). Kč 25,1720<span class=\"text-accent-green-light dark:text-accent-green-dark\"> Kč 0,0180 (0.07%) </span></span><span class=\"text-base sm:text-lg font-bold\">Gold price on market. 2 929,7250 €<span class=\"text-[#B72D2D]\"> -6,9560 € (-0.24%) </span></span><span class=\"text-base sm:text-lg font-bold\">Silver price on market. 31,5280 €<span class=\"text-accent-green-light dark:text-accent-green-dark\"> 0,1690 € (0.54%) </span></span><span class=\"text-base sm:text-lg font-bold\">PLN price (to EUR). zł 4,2660<span class=\"text-[#B72D2D]\"> zł -0,0050 (-0.12%) </span></span>"
|
||||||
|
const productStore = useProductStore()
|
||||||
|
const activeElement = ref(1);
|
||||||
|
productStore.getModules()
|
||||||
|
|
||||||
|
// Computed property to duplicate the content
|
||||||
|
const duplicatedContent = computed(() => {
|
||||||
|
const originalContent = res || '';
|
||||||
|
return originalContent + originalContent + originalContent + originalContent;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const changeActive = (item: number) => {
|
||||||
|
activeElement.value = item;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.price-container {
|
||||||
|
/* display: flex; */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
display: flex;
|
||||||
|
gap: 64px;
|
||||||
|
animation: slidein 40s linear infinite;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logos {
|
||||||
|
display: flex;
|
||||||
|
gap: 64px;
|
||||||
|
width: 100%;
|
||||||
|
/* display: inline-block; */
|
||||||
|
margin: 0px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fas {
|
||||||
|
height: 50%;
|
||||||
|
animation: fade-in 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slidein {
|
||||||
|
from {
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translate3d(-100%, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 900px) {
|
||||||
|
.slider {
|
||||||
|
animation: slidein 80s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
41
components/section/shop-page/Product.vue
Normal file
41
components/section/shop-page/Product.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="w-[150px] sm:w-[260px] md:w-[330px] px-2 py-3 sm:py-5 sm:px-[15px] bg-block rounded-2xl flex flex-col items-center gap-[15px] sm:gap-[50px]">
|
||||||
|
<img :src="`https://www.yourgold.cz/api/public/file/${props.product?.cover_picture_uuid}.webp`" alt="pics"
|
||||||
|
class="max-h-[95px] sm:max-h-[180px] md:max-h-[205px] rounded-[5px]" />
|
||||||
|
<div class="flex flex-col justify-between h-full w-full gap-[7px] sSm:gap-[15px]"
|
||||||
|
@click="productStore.addToCart(props.product)">
|
||||||
|
<div class="flex flex-col gap-[7px] sm:gap-[15px] w-full">
|
||||||
|
<h3 class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] text-bg-dark">
|
||||||
|
{{ props.product?.name }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-[9px] sm:text-[12px] text-sm text-bg-dark">
|
||||||
|
{{ props.product?.tax_name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<p class="text-accent-green-light font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold">
|
||||||
|
{{ props.product?.formatted_price }}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
class="w-[22px] h-[22px] sm:w-9 sm:h-9 md:w-12 md:h-12 rounded-[5px] sm:rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center p-1">
|
||||||
|
<UButton icon="i-lucide-shopping-cart" variant="ghost" class="sm:text-[25px] md:text-2xl text-bg-light" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// import imgUrl from "~/utils/imgUrl";
|
||||||
|
const props = defineProps({
|
||||||
|
product: Object,
|
||||||
|
});
|
||||||
|
|
||||||
|
const productStore = useProductStore()
|
||||||
|
|
||||||
|
// const addToCartAndPreventNavigation = (event: any) => {
|
||||||
|
// event.preventDefault();
|
||||||
|
// useCartStore().addToCart(props.product);
|
||||||
|
// };
|
||||||
|
</script>
|
434
components/section/shop-page/ShopMain.vue
Normal file
434
components/section/shop-page/ShopMain.vue
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
<template>
|
||||||
|
<SectionShopPageCurrencyRatesBar class="mb-[25px] sm:mb-[55px] xl:mb-[75px]" />
|
||||||
|
<UiContainer>
|
||||||
|
<div class="flex flex-col gap-[25px] sm:gap-10 xl:flex-row">
|
||||||
|
<!-- button to open categories -->
|
||||||
|
<div class="xl:hidden flex items-center w-full">
|
||||||
|
<button @click="openCategories = !openCategories"
|
||||||
|
class="h-[40px] w-full cursor-pointer rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px] bg-button text-text-dark group-hover:bg-button-hover">
|
||||||
|
Otevřené kategorie a filtry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Transition>
|
||||||
|
<div v-if="openCategories" class="min-w-[250px] px-5 sm:p-0 xl:hidden">
|
||||||
|
<h1 class="font-bounded leading-[140%] font-bold text-[24px] mb-[25px]">
|
||||||
|
{{ $t("category") }}
|
||||||
|
</h1>
|
||||||
|
<div class="flex flex-col gap-[25px]">
|
||||||
|
<div>
|
||||||
|
<div v-if="categoriesList.length < 1" class="animate-pulse">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
||||||
|
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
||||||
|
<div class="w-4 h-4 bg-gray-200 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CategoryTree :data="categoriesList" @change-category="changeCategory($event)"
|
||||||
|
:active="categoryId" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="mb-[25px] text-lg font-extrabold text-black dark:text-white">
|
||||||
|
{{ $t("filtered_by") }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div v-for="(item, itemIndex) in filters" :key="itemIndex"
|
||||||
|
:class="['mb-[30px]', visibleFeatures[item.feature] && 'border-b border-block pb-2']">
|
||||||
|
<span class="flex justify-between items-center font-bold cursor-pointer mb-[25px] text-base"
|
||||||
|
@click="toggleFeature(item.feature)">
|
||||||
|
{{ item.feature }}
|
||||||
|
<span :class="[visibleFeatures[item.feature] && 'rotate-180', 'transition-all']"><i
|
||||||
|
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
|
||||||
|
</span>
|
||||||
|
<ul v-show="visibleFeatures[item.feature]" class="flex flex-col gap-5">
|
||||||
|
<li v-for="filter in item.feature_values" :key="filter.value_id"
|
||||||
|
class="flex items-center gap-[10px] cursor-pointer">
|
||||||
|
|
||||||
|
<input :id="`${filter.value_id}`" :value="`${filter.parent}.${filter.value_id}`"
|
||||||
|
v-model="selectedFilters" type="checkbox"
|
||||||
|
class="border-button !bg-inherit" />
|
||||||
|
<label :for="`${filter.value_id}`"
|
||||||
|
class="cursor-pointer flex items-center justify-between w-full text-base">
|
||||||
|
<span>{{ filter.value }}</span>
|
||||||
|
<span>12</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<!-- categories -->
|
||||||
|
<div class="min-w-[250px] hidden xl:block">
|
||||||
|
<h1 class="font-bounded leading-[140%] font-bold text-[40px] mb-[55px]">
|
||||||
|
{{ $t("category") }}
|
||||||
|
</h1>
|
||||||
|
<div class="flex flex-col gap-12">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div v-if="categoriesList.length < 1" class="animate-pulse">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24">
|
||||||
|
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
||||||
|
<div class="w-4 h-4 bg-gray-200 rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CategoryTree :data="categoriesList" @change-category="changeCategory($event)"
|
||||||
|
:active="categoryId" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="mb-10 text-2xl font-extrabold text-black dark:text-white">
|
||||||
|
{{ $t("filtered_by") }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- <div v-if="filters.length < 1" class="mb-8 text-white animate-pulse">
|
||||||
|
<div v-for="i in 5"
|
||||||
|
class="mt-10 flex justify-between font-bold text-black cursor-pointer 2xl:pr-24 dark:text-gray">
|
||||||
|
<div class="w-32 h-4 bg-gray-200 rounded"></div>
|
||||||
|
<div class="w-4 h-4 bg-gray-200 rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div v-for="(item, itemIndex) in filters" :key="itemIndex"
|
||||||
|
:class="['mb-[30px]', visibleFeatures[item.feature] && 'border-b border-block pb-2']">
|
||||||
|
<span class="flex justify-between items-center font-bold cursor-pointer mb-[25px]"
|
||||||
|
@click="toggleFeature(item.feature)">
|
||||||
|
{{ item.feature }}
|
||||||
|
<span :class="[visibleFeatures[item.feature] && 'rotate-180', 'transition-all']"><i
|
||||||
|
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
<ul v-show="visibleFeatures[item.feature]" class="flex flex-col gap-5">
|
||||||
|
<li v-for="filter in item.feature_values" :key="filter.value_id"
|
||||||
|
class="flex items-center gap-[10px] cursor-pointer">
|
||||||
|
<!-- <input :id="`${filter.value_id}`" :value="`${filter.parent}.${filter.value_id}`"
|
||||||
|
v-model="selectedFilters" type="checkbox" class="border-button !bg-inherit" />
|
||||||
|
<label :for="`${filter.value_id}`" class="cursor-pointer">{{ filter.value }}</label> -->
|
||||||
|
|
||||||
|
|
||||||
|
<input :id="`${filter.value_id}`" :value="`${filter.parent}.${filter.value_id}`"
|
||||||
|
v-model="selectedFilters" type="checkbox" class="border-button !bg-inherit" />
|
||||||
|
<label :for="`${filter.value_id}`"
|
||||||
|
class="cursor-pointer flex items-center justify-between w-full">
|
||||||
|
<span>{{ filter.value }}</span>
|
||||||
|
<span>12</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full space-y-10">
|
||||||
|
|
||||||
|
<!-- pop-up -->
|
||||||
|
<div v-if="isInfo"
|
||||||
|
class="w-full xl:w-[70%] mx-auto border-y border-block py-[15px] sm:p-[30px] flex gap-[55px] relative">
|
||||||
|
<UButton @click="closeElement()" size="xl" icon="i-lucide-x" variant="ghost"
|
||||||
|
class="p-0 absolute right-0 top-2 sm:right-2 sm:top-2 cursor-pointer text-button font-light hover:bg-inherit hover:text-button-hover" />
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row gap-[25px]">
|
||||||
|
<div class="flex flex-col justify-between gap-[25px]">
|
||||||
|
<h4 class="font-inter text-lg sm:text-[24px] leading-[150%] md:leading-[120%] font-bold">
|
||||||
|
{{ component.section_lang_data.title }}
|
||||||
|
</h4>
|
||||||
|
<p>{{ component.section_lang_data.description }}</p>
|
||||||
|
</div>
|
||||||
|
<img class="max-w-[150px] mx-auto"
|
||||||
|
:src="`/api/files/${component.image_collection}/${component.section_id}/${component.section_img[0]}?thumb=640x0')`"
|
||||||
|
alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="products.length < 1" class="grid gap-12 pt-32 pb-16 md:grid-cols-2 2xl:grid-cols-3">
|
||||||
|
<TheProductSkeleton v-for="index in 6"></TheProductSkeleton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- products -->
|
||||||
|
<div v-else ref="loadingElement" class="flex flex-wrap justify-center gap-5 sm:gap-10">
|
||||||
|
<Product v-for="product in products" :key="product.id" :product="product" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="reachedEnd" class="w-full flex justify-center">
|
||||||
|
<p>
|
||||||
|
{{ $t("FrontTranslations", "You reached end of the list.") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UiContainer>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import Product from "./Product.vue";
|
||||||
|
import type { Feature, ProductType } from "~/types";
|
||||||
|
import CategoryTree from "./CategoryTree.vue";
|
||||||
|
|
||||||
|
defineProps<{ component: Component }>();
|
||||||
|
type Component = {
|
||||||
|
image_collection: string;
|
||||||
|
section_id: string;
|
||||||
|
section_img: string;
|
||||||
|
section_lang_data: {
|
||||||
|
title: string;
|
||||||
|
description: string
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCategories = ref(false);
|
||||||
|
const isInfo = ref<boolean>(true);
|
||||||
|
const selectedFilters = ref<any>([]);
|
||||||
|
const categoryId = ref<number>(1);
|
||||||
|
const itemsCount = ref(0);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const reachedEnd = ref(false);
|
||||||
|
|
||||||
|
const loadingElement = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
const page = ref(1);
|
||||||
|
const elems = ref(12);
|
||||||
|
const maxElements = ref(0);
|
||||||
|
|
||||||
|
const products = ref([] as ProductType[]);
|
||||||
|
async function getProducts() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?p=${page.value}&elems=${elems.value}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
products.value = data.data.items;
|
||||||
|
maxElements.value = data.data.items_count + 1;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getProducts error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = ref([] as Feature[]);
|
||||||
|
async function getCategory() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/products/category/1/classification`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
filters.value = data.data as Feature[];
|
||||||
|
filters.value.forEach((el) => {
|
||||||
|
const parentId = el.feature_id;
|
||||||
|
el.feature_values.forEach((el) => {
|
||||||
|
el.parent = parentId;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getCategory error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoriesList = ref([]);
|
||||||
|
async function getCategoryTree() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/categories/tree`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
categoriesList.value = data.data.children;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getCategory error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getProducts()
|
||||||
|
getCategory()
|
||||||
|
getCategoryTree()
|
||||||
|
|
||||||
|
const closeElement = () => {
|
||||||
|
isInfo.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener("scroll", scrollEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("scroll", scrollEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function scrollEvent(e: Event) {
|
||||||
|
let maxScrollY = window.scrollY || document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
||||||
|
|
||||||
|
if (window.scrollY >= maxScrollY - 500 && !reachedEnd.value && !loading.value) {
|
||||||
|
loading.value = true;
|
||||||
|
await loadMoreProducts();
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.value.forEach((item) => {
|
||||||
|
visibleFeatures[item.feature] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleFeatures = reactive<any>({});
|
||||||
|
function toggleFeature(feature: any) {
|
||||||
|
if (visibleFeatures.hasOwnProperty(feature)) {
|
||||||
|
visibleFeatures[feature] = !visibleFeatures[feature];
|
||||||
|
} else {
|
||||||
|
visibleFeatures[feature] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilteredQueryString extends URLSearchParams {
|
||||||
|
override append(name: string, value: string): void {
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.append(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMoreProducts() {
|
||||||
|
let qParams = new FilteredQueryString();
|
||||||
|
|
||||||
|
page.value = page.value + 1;
|
||||||
|
|
||||||
|
qParams.append("p", `${page.value}`);
|
||||||
|
qParams.append("elems", `${elems.value}`);
|
||||||
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
maxElements.value = data.data.items_count;
|
||||||
|
|
||||||
|
if (data.data.items) {
|
||||||
|
products.value.push(...(data.data.items as ProductType[]));
|
||||||
|
} else {
|
||||||
|
reachedEnd.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (products.value.length >= maxElements.value) {
|
||||||
|
reachedEnd.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getCategory error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeCategory = (item: any) => {
|
||||||
|
categoryId.value = item.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(selectedFilters, async (newQuestion) => {
|
||||||
|
if (newQuestion) {
|
||||||
|
page.value = 1;
|
||||||
|
reachedEnd.value = false;
|
||||||
|
loadingElement.value?.scrollIntoView();
|
||||||
|
|
||||||
|
let qParams = new FilteredQueryString();
|
||||||
|
|
||||||
|
qParams.append("p", `${page.value}`);
|
||||||
|
qParams.append("elems", `${elems.value}`);
|
||||||
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/products/category/1?${qParams.toString()}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
products.value = data.data.items;
|
||||||
|
maxElements.value = data.data.items_count;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("selectedFilters error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(categoryId, async (newQuestion) => {
|
||||||
|
if (newQuestion) {
|
||||||
|
page.value = 1;
|
||||||
|
reachedEnd.value = false;
|
||||||
|
loadingElement.value?.scrollIntoView();
|
||||||
|
|
||||||
|
let qParams = new FilteredQueryString();
|
||||||
|
|
||||||
|
qParams.append("p", `${page.value}`);
|
||||||
|
qParams.append("elems", `${elems.value}`);
|
||||||
|
qParams.append("features", selectedFilters.value.length > 0 ? selectedFilters.value : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
products.value = data.data.items;
|
||||||
|
maxElements.value = data.data.items_count;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getCategory error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.v-enter-active,
|
||||||
|
.v-leave-active {
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-enter-from,
|
||||||
|
.v-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<UContainer
|
<UContainer
|
||||||
class="mx-auto w-full max-w-[360px] px-4 sm:max-w-[768px] sm:px-[17px] md:max-w-[1000px] md:px-6 xl:max-w-[1920px] xl:px-20">
|
class="mx-auto w-full max-w-[380px] px-4 sm:max-w-[768px] sm:px-[17px] md:max-w-[1000px] md:px-6 xl:max-w-[1920px] xl:px-20">
|
||||||
<slot />
|
<slot />
|
||||||
</UContainer>
|
</UContainer>
|
||||||
</template>
|
</template>
|
||||||
|
72
composables/useMyFetch.ts
Normal file
72
composables/useMyFetch.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { ofetch } from "ofetch";
|
||||||
|
|
||||||
|
export interface RequestOptions<T> extends RequestInit {
|
||||||
|
onError?: (error: Error, statusCode: number) => void;
|
||||||
|
onSuccess?: (data: T, statusCode: number) => void;
|
||||||
|
onStart?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function useMyFetch
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Makes a request to a given url, handles cookies and errors.
|
||||||
|
*
|
||||||
|
* @param {string} url - The url to make a request to.
|
||||||
|
* @param {RequestOptions} [options] - The options to use for the request.
|
||||||
|
*
|
||||||
|
* @returns {Promise<T | undefined>} - A promise resolving to the response data
|
||||||
|
* or undefined if an error occurred.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data } = useMyFetch<{ name: string }>('/api/user')
|
||||||
|
*/
|
||||||
|
export const useMyFetch = async <T>(
|
||||||
|
url: string,
|
||||||
|
options?: RequestOptions<T>
|
||||||
|
): Promise<T | undefined> => {
|
||||||
|
if (options?.onStart) options.onStart();
|
||||||
|
let response = null;
|
||||||
|
try {
|
||||||
|
const event = useRequestEvent();
|
||||||
|
|
||||||
|
if (options == null) options = {};
|
||||||
|
|
||||||
|
options.credentials = "include";
|
||||||
|
|
||||||
|
if (import.meta.server) {
|
||||||
|
const api_uri =
|
||||||
|
event?.node.req.headers["api-uri"] || "http://localhost:4000";
|
||||||
|
url = api_uri + url;
|
||||||
|
options.headers = event?.headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await ofetch.raw(url, options);
|
||||||
|
if (import.meta.server && !event?.handled) {
|
||||||
|
for (const cookie of response.headers.getSetCookie()) {
|
||||||
|
event?.headers.set("Cookie", cookie);
|
||||||
|
event?.node.res.setHeader("set-cookie", cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle errors if any
|
||||||
|
if (!response.ok && typeof options.onError == "function") {
|
||||||
|
options.onError(new Error(response.statusText), response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle success to be able clearly marked that request has finished
|
||||||
|
if (response.ok && typeof options.onSuccess == "function") {
|
||||||
|
options.onSuccess(response._data.data, response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response._data as T;
|
||||||
|
} catch (e) {
|
||||||
|
// handle errors if any
|
||||||
|
if (typeof options?.onError == "function") {
|
||||||
|
options.onError(e as Error, response?.status || 500);
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
@ -23,4 +23,4 @@ onMounted(() => {
|
|||||||
useHead(menuStore.headMeta);
|
useHead(menuStore.headMeta);
|
||||||
|
|
||||||
const componentsList = await store.getComponents(route.params.id);
|
const componentsList = await store.getComponents(route.params.id);
|
||||||
</script>
|
</script>
|
@ -28,5 +28,4 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
@ -139,7 +139,8 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
name: `id-slug___${$i18n.locale.value}`,
|
name: `id-slug___${$i18n.locale.value}`,
|
||||||
});
|
});
|
||||||
openDropDown.value = false;
|
openDropDown.value = false;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
router.push({
|
router.push({
|
||||||
params: {
|
params: {
|
||||||
slug: defaultMenu.value.link_rewrite,
|
slug: defaultMenu.value.link_rewrite,
|
||||||
@ -150,6 +151,28 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function navigateToShop() {
|
||||||
|
navigateToItem(menuItems.value?.items.find(item => item.page_name === 'shop'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// function redirectToPage(link_rewrite: string) {
|
||||||
|
// const page = menuItems.value?.items.find(
|
||||||
|
// (item) => item.link_rewrite === link_rewrite
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (!page?.id_page || !page?.link_rewrite) {
|
||||||
|
// console.warn(`Page not found or missing data for name: ${link_rewrite}`);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// router.push({
|
||||||
|
// params: {
|
||||||
|
// id: page?.id_page,
|
||||||
|
// slug: page?.link_rewrite,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
const getFirstImage = () => {
|
const getFirstImage = () => {
|
||||||
const req = useRequestEvent();
|
const req = useRequestEvent();
|
||||||
const url = useRequestURL();
|
const url = useRequestURL();
|
||||||
@ -209,24 +232,6 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
function redirectToPage(link_rewrite: string) {
|
|
||||||
const page = menuItems.value?.items.find(
|
|
||||||
(item) => item.link_rewrite === link_rewrite
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!page?.id_page || !page?.link_rewrite) {
|
|
||||||
console.warn(`Page not found or missing data for name: ${link_rewrite}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push({
|
|
||||||
params: {
|
|
||||||
id: page?.id_page,
|
|
||||||
slug: page?.link_rewrite,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
watch($i18n.locale, async () => {
|
watch($i18n.locale, async () => {
|
||||||
await loadMenu();
|
await loadMenu();
|
||||||
await loadFooter();
|
await loadFooter();
|
||||||
@ -246,13 +251,14 @@ export const useMenuStore = defineStore("menuStore", () => {
|
|||||||
selectedCountry,
|
selectedCountry,
|
||||||
selectedCurrency,
|
selectedCurrency,
|
||||||
selectedPhoneCountry,
|
selectedPhoneCountry,
|
||||||
|
defaultMenu,
|
||||||
|
headMeta,
|
||||||
|
navigateToShop,
|
||||||
loadMenu,
|
loadMenu,
|
||||||
loadFooter,
|
loadFooter,
|
||||||
getCountryList,
|
getCountryList,
|
||||||
navigateToItem,
|
navigateToItem,
|
||||||
redirectToPage,
|
// redirectToPage,
|
||||||
getCurrencies,
|
getCurrencies
|
||||||
defaultMenu,
|
|
||||||
headMeta,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export const useProductStore = defineStore("productStore", () => {
|
export const useProductStore = defineStore("productStore", () => {
|
||||||
const productList = ref();
|
const productList = ref();
|
||||||
|
const modules = ref();
|
||||||
|
|
||||||
async function getList(count: number, categoryId = 1) {
|
async function getList(count: number, categoryId = 1) {
|
||||||
try {
|
try {
|
||||||
@ -23,8 +24,82 @@ export const useProductStore = defineStore("productStore", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getModules() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/module/e_shop`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
modules.value = data.children.find(
|
||||||
|
(item: { id: number; name: string }) =>
|
||||||
|
item.name === "currency_rates_bar"
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getList error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addToCart(product) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`http://127.0.0.1:4000/api/public/user/cart/item/add/${product.id}/1`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
console.log(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getList error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = ref();
|
||||||
|
async function getCart() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`http://127.0.0.1:4000/api/public/user/cart`, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
cart.value = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getList error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
productList,
|
productList,
|
||||||
|
modules,
|
||||||
getList,
|
getList,
|
||||||
|
getModules,
|
||||||
|
addToCart,
|
||||||
|
getCart,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,10 @@ export const useStore = defineStore("store", () => {
|
|||||||
const totalInvestment = ref()
|
const totalInvestment = ref()
|
||||||
const minValue = ref()
|
const minValue = ref()
|
||||||
|
|
||||||
|
// login
|
||||||
|
const email = ref()
|
||||||
|
const password = ref()
|
||||||
|
|
||||||
const components = ref({} as PBPageItem[]);
|
const components = ref({} as PBPageItem[]);
|
||||||
const getSections = async (id: string) => {
|
const getSections = async (id: string) => {
|
||||||
pb.cancelRequest("menu_view");
|
pb.cancelRequest("menu_view");
|
||||||
@ -111,6 +115,34 @@ export const useStore = defineStore("store", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function logIn() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
'http://127.0.0.1:4000/api/public/user/session/start',
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
mail: email.value,
|
||||||
|
password: password.value
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
minValue.value = data.data
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getList error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getCalculator()
|
getCalculator()
|
||||||
getMinValue()
|
getMinValue()
|
||||||
|
|
||||||
@ -121,6 +153,9 @@ export const useStore = defineStore("store", () => {
|
|||||||
monthlySavings,
|
monthlySavings,
|
||||||
storagePeriod,
|
storagePeriod,
|
||||||
minValue,
|
minValue,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
logIn,
|
||||||
getCalculator,
|
getCalculator,
|
||||||
getComponents,
|
getComponents,
|
||||||
getSections,
|
getSections,
|
||||||
|
@ -126,4 +126,34 @@ export type Currencies = {
|
|||||||
sign: string;
|
sign: string;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
suffix: boolean;
|
suffix: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FeatureValue = {
|
||||||
|
parent: number,
|
||||||
|
products_with_value: number,
|
||||||
|
value: string,
|
||||||
|
value_id: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Feature = {
|
||||||
|
feature: string,
|
||||||
|
feature_id: number,
|
||||||
|
feature_values: FeatureValue[],
|
||||||
|
products_with_feature: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductType = {
|
||||||
|
applied_tax_rate: number,
|
||||||
|
cover_picture_uuid: string,
|
||||||
|
description: string,
|
||||||
|
formatted_price: string,
|
||||||
|
id: number,
|
||||||
|
in_stock: number,
|
||||||
|
is_sale_active: boolean,
|
||||||
|
link_rewrite: string,
|
||||||
|
name: string,
|
||||||
|
price: number,
|
||||||
|
tax_name: string,
|
||||||
|
cart_item_id?: number
|
||||||
|
product_id?: number
|
||||||
}
|
}
|
Reference in New Issue
Block a user