shop
This commit is contained in:
@ -7,7 +7,7 @@
|
||||
}" />
|
||||
<div class="w-full sm:w-[80%] mx-auto my-auto xl:w-full xl:px-12 ">
|
||||
<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>
|
||||
<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">{{
|
||||
@ -15,28 +15,28 @@
|
||||
</div>
|
||||
<div class="space-y-[15px]">
|
||||
<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" />
|
||||
</div>
|
||||
<div class="space-y-[15px]">
|
||||
<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" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-button hover:text-button-hover transition-all font-medium mt-[30px] cursor-pointer">{{
|
||||
$t('forgot_password_question')
|
||||
}}</p>
|
||||
}}</p>
|
||||
<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 class="mt-[25px] sm:mt-[30px] w-full flex justify-center gap-3">
|
||||
<p class="cursor-pointer hover:underline transition-all">{{
|
||||
$t('no_account')
|
||||
}}</p>
|
||||
}}</p>
|
||||
<p class="text-button cursor-pointer hover:text-button-hover">{{
|
||||
$t('sign_up_now')
|
||||
}}</p>
|
||||
}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</UiContainer>
|
||||
@ -49,4 +49,6 @@ type Component = {
|
||||
section_id: string;
|
||||
section_img: string;
|
||||
section_lang_data: {};
|
||||
};</script>
|
||||
};
|
||||
const store = useStore()
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<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="{
|
||||
backgroundImage: `url('/api/files/${component.image_collection}/${component.section_id}/${component.section_img[0]}?thumb=1200x0')`,
|
||||
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">{{
|
||||
$t('back_to_home') }}</button>
|
||||
</div>
|
||||
<div class="space-y-[30px]">
|
||||
<div class="space-y-[25px] sm:space-y-[30px]">
|
||||
<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]">
|
||||
<p class="pl-6">{{ $t('first_name') }}</p>
|
||||
<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 class="space-y-[15px]">
|
||||
<p class="pl-6">{{ $t('last_name') }}</p>
|
||||
<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 class="space-y-[15px]">
|
||||
<p class="pl-6">{{ $t('email') }}</p>
|
||||
<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 class="space-y-[15px]">
|
||||
<div class="space-y-[15px]" ref="dropdownRef">
|
||||
<p class="pl-6">{{ $t('phone') }}</p>
|
||||
<div class="flex items-center border-2 border-block rounded-lg">
|
||||
<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="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
|
||||
{{ menuStore.selectedPhoneCountry.name }} <span> <i
|
||||
class="flex items-center gap-2 text-sm sm:text-xl uppercase text-text-light dark:text-text-dark">
|
||||
<span :class="[dropCountry && 'rotate-180', 'transition-all']"> <i
|
||||
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 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]">
|
||||
<div class="overflow-auto h-[200px] w-full">
|
||||
<p @click="() => {
|
||||
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-y-auto h-[200px] w-full">
|
||||
<p v-for="item in menuStore.countryList" @click="() => {
|
||||
menuStore.selectedPhoneCountry = item
|
||||
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]"
|
||||
v-for="item in menuStore.countryList">
|
||||
}" 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]"
|
||||
:title="item.name">
|
||||
{{ item.name }}
|
||||
</p>
|
||||
</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"
|
||||
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 class="space-y-[15px]">
|
||||
<p class="pl-6">{{ $t('email') }}</p>
|
||||
<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" />
|
||||
<p class="pl-6">{{ $t('account_type') }}</p>
|
||||
<USelect v-model="selectedType" :items="component.section_lang_data.account_types"
|
||||
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 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"
|
||||
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>
|
||||
@ -91,13 +125,26 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps<{ component: Component }>();
|
||||
import { onClickOutside } from "@vueuse/core";
|
||||
const props = defineProps<{ component: Component }>();
|
||||
type Component = {
|
||||
image_collection: string;
|
||||
section_id: string;
|
||||
section_img: string;
|
||||
section_lang_data: {};
|
||||
section_lang_data: {
|
||||
account_types: [{
|
||||
id: number,
|
||||
name: string
|
||||
}]
|
||||
};
|
||||
};
|
||||
const menuStore = useMenuStore()
|
||||
const dropdownRef = ref(null);
|
||||
const dropCountry = ref()
|
||||
|
||||
const selectedType = ref(props.component.section_lang_data.account_types[0].name)
|
||||
|
||||
onClickOutside(dropdownRef, () => {
|
||||
dropCountry.value = false
|
||||
});
|
||||
</script>
|
53
components/section/shop-page/Product.vue
Normal file
53
components/section/shop-page/Product.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<NuxtLink>
|
||||
<li class="bg-block rounded-2xl h-[420px] w-full 2xl:max-w-[348px] list-none overflow-hidden p-5">
|
||||
<article class="group h-full">
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="h-[205px]">
|
||||
<img :src="`https://www.yourgold.cz/api/public/file/${props.product?.cover_picture_uuid}.webp`"
|
||||
:alt="props.product?.description" class="h-full object-contain w-auto block mx-auto">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-start text-start mt-[50px] justify-between h-full">
|
||||
<div class="space-y-[15px]">
|
||||
<h4 class="font-bold">{{ props.product?.name }}</h4>
|
||||
<p class="text-sm font-normal">{{ props.product?.tax_name }}</p>
|
||||
</div>
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<p class="font-bold text-accent-green-light text-sm sm:text-2xl">{{ props.product?.formatted_price }}</p>
|
||||
<div class="w-12 h-12 bg-button flex items-center justify-center rounded-[10px]">
|
||||
<i class="uil uil-shopping-cart text-[31px] cursor-pointer text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!props.product?.is_sale_active"
|
||||
class="absolute top-0 flex items-center justify-center w-full h-full px-4 bg-black bg-opacity-90 cursor-progress">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 48 48">
|
||||
<path fill="#ffcc00" d="M5.7 22H42.5V26H5.7z" transform="rotate(-45.001 24.036 24.037)"></path>
|
||||
<path fill="#ffcc00"
|
||||
d="M24,4C13,4,4,13,4,24s9,20,20,20s20-9,20-20S35,4,24,4z M24,40c-8.8,0-16-7.2-16-16S15.2,8,24,8 s16,7.2,16,16S32.8,40,24,40z">
|
||||
</path>
|
||||
</svg>
|
||||
<span class="font-bold text-center uppercase text-yellow">
|
||||
{{ $t("FrontTranslations", "not available") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</li>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import imgUrl from "~/utils/imgUrl";
|
||||
const props = defineProps({
|
||||
product: Object,
|
||||
});
|
||||
|
||||
const addToCartAndPreventNavigation = (event: any) => {
|
||||
event.preventDefault();
|
||||
useCartStore().addToCart(props.product);
|
||||
};
|
||||
</script>
|
302
components/section/shop-page/ShopMain.vue
Normal file
302
components/section/shop-page/ShopMain.vue
Normal file
@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<div class="container block mx-auto">
|
||||
<div class="flex flex-col gap-16 px-4 xl:flex-row">
|
||||
<BaseButtonLogin @click="openCategories = !openCategories" class="xl:hidden">{{
|
||||
$t("FrontTranslations", "Open categories & filters") }}</BaseButtonLogin>
|
||||
<Transition>
|
||||
<div v-if="openCategories" class="z-40 block w-full pt-8 xl:w-1/4 xl:pt-36 xl:hidden">
|
||||
<BaseTitle>
|
||||
{{ $t("FrontTranslations", "Categories") }}
|
||||
</BaseTitle>
|
||||
<div class="mt-14">
|
||||
<div class="flex flex-col gap-12">
|
||||
<div>
|
||||
<CategoryTree :data="categoriesList" @change-category="changeCategory($event)"
|
||||
:active="categoryId" />
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-8 text-2xl font-extrabold text-black dark:text-white">
|
||||
{{ $t("FrontTranslations", "Filtered by") }}
|
||||
</p>
|
||||
|
||||
<div v-for="(item, itemIndex) in filters" :key="itemIndex" class="mb-8 text-white">
|
||||
<span
|
||||
class="flex justify-between font-bold text-black cursor-pointer 2xl:pr-24 dark:text-gray"
|
||||
@click="toggleFeature(item.feature)">
|
||||
{{ item.feature }}
|
||||
<span class="w-4 h-4 text-yellow"><i
|
||||
class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
|
||||
</span>
|
||||
<ul v-show="visibleFeatures[item.feature]"
|
||||
class="flex flex-col gap-8 pl-8 mt-8 text-black dark:text-gray">
|
||||
<li v-for="filter in item.feature_values" :key="filter.value_id"
|
||||
class="flex gap-1">
|
||||
<input :id="`${filter.value_id}`"
|
||||
:value="`${filter.parent}.${filter.value_id}`" v-model="selectedFilters"
|
||||
type="checkbox" />
|
||||
<label :for="`${filter.value_id}`">{{ filter.value }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="z-40 hidden w-full pt-8 xl:w-1/4 xl:pt-36 xl:block">
|
||||
<BaseTitle>
|
||||
{{ $t("FrontTranslations", "Categories") }}
|
||||
</BaseTitle>
|
||||
<div class="mt-14 top-10">
|
||||
<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-8 text-2xl font-extrabold text-black dark:text-white">
|
||||
{{ $t("FrontTranslations", "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-8 text-white">
|
||||
<span
|
||||
class="flex justify-between font-bold text-black cursor-pointer 2xl:pr-24 dark:text-gray"
|
||||
@click="toggleFeature(item.feature)">
|
||||
{{ item.feature }}
|
||||
<span class="w-4 h-4 text-yellow"><i
|
||||
class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
|
||||
</span>
|
||||
<ul v-show="visibleFeatures[item.feature]"
|
||||
class="flex flex-col gap-8 pl-8 mt-8 text-black dark:text-gray">
|
||||
<li v-for="filter in item.feature_values" :key="filter.value_id" class="flex gap-1">
|
||||
<input :id="`${filter.value_id}`" :value="`${filter.parent}.${filter.value_id}`"
|
||||
v-model="selectedFilters" type="checkbox" />
|
||||
<label :for="`${filter.value_id}`">{{ filter.value }}</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full pt-20 xl:w-3/4">
|
||||
<ThePartnerInfo v-if="isInfo" @close-element="closeElement()" />
|
||||
|
||||
<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>
|
||||
|
||||
<div v-else ref="loadingElement" class="grid gap-12 pt-32 pb-16 md:grid-cols-2 2xl:grid-cols-3">
|
||||
<Product v-for="product in products" :key="product.id" :product="product" />
|
||||
<div v-if="reachedEnd"
|
||||
class="md:col-span-2 2xl:col-span-3 border-2 border-yellow border-r-0 border-l-0 mt-10">
|
||||
<p class="text-black dark:text-gray text-center text-lg p-2">
|
||||
{{ $t("FrontTranslations", "You reached end of the list.") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
// import type { Product, Feature } from "~/types";
|
||||
import Product from "./Product.vue";
|
||||
const openCategories = ref(false);
|
||||
const isInfo = ref<boolean>(true);
|
||||
const selectedFilters = ref<any>([]);
|
||||
const products = ref([] as Product[]);
|
||||
const filters = ref([] as Feature[]);
|
||||
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);
|
||||
|
||||
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("getList error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
getProducts()
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// fetchWithCookie(useRequestEvent(), `public/products/category/1/classification`).then((res) => {
|
||||
// filters.value = res._data.data as Feature[];
|
||||
// filters.value.forEach((el) => {
|
||||
// const parentId = el.feature_id;
|
||||
// el.feature_values.forEach((el) => {
|
||||
// el.parent = parentId;
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
const visibleFeatures = reactive<any>({});
|
||||
|
||||
filters.value.forEach((item) => {
|
||||
visibleFeatures[item.feature] = false;
|
||||
});
|
||||
|
||||
function toggleFeature(feature: any) {
|
||||
if (visibleFeatures.hasOwnProperty(feature)) {
|
||||
visibleFeatures[feature] = !visibleFeatures[feature];
|
||||
} else {
|
||||
visibleFeatures[feature] = true;
|
||||
}
|
||||
}
|
||||
|
||||
class FilteredQueryString extends URLSearchParams {
|
||||
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);
|
||||
|
||||
// await fetchWithCookie(useRequestEvent(), `public/products/category/${categoryId.value}?` + qParams.toString()).then((res) => {
|
||||
// maxElements.value = res._data.data.items_count;
|
||||
|
||||
// if (res._data.data.items) {
|
||||
// products.value.push(...(res._data.data.items as Product[]));
|
||||
// } else {
|
||||
// reachedEnd.value = true;
|
||||
// }
|
||||
|
||||
// if (products.value.length >= maxElements.value) {
|
||||
// reachedEnd.value = true;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
const categoriesList = ref([]);
|
||||
// fetchWithCookie(useRequestEvent(), `public/categories/tree`).then((res) => {
|
||||
// categoriesList.value = res._data.data.children;
|
||||
// });
|
||||
|
||||
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);
|
||||
|
||||
// fetchWithCookie(useRequestEvent(), `public/products/category/1?` + qParams.toString()).then((res) => {
|
||||
// products.value = res._data.data.items;
|
||||
// maxElements.value = res._data.data.items_count;
|
||||
// });
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
// fetchWithCookie(useRequestEvent(), `public/products/category/${categoryId.value}?` + qParams.toString()).then((res) => {
|
||||
// products.value = res._data.data.items;
|
||||
// maxElements.value = res._data.data.items_count;
|
||||
// });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-enter-active,
|
||||
.v-leave-active {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.v-enter-from,
|
||||
.v-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
@ -13,6 +13,10 @@ export const useStore = defineStore("store", () => {
|
||||
const totalInvestment = ref()
|
||||
const minValue = ref()
|
||||
|
||||
// login
|
||||
const email = ref()
|
||||
const password = ref()
|
||||
|
||||
const components = ref({} as PBPageItem[]);
|
||||
const getSections = async (id: string) => {
|
||||
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()
|
||||
getMinValue()
|
||||
|
||||
@ -121,6 +153,9 @@ export const useStore = defineStore("store", () => {
|
||||
monthlySavings,
|
||||
storagePeriod,
|
||||
minValue,
|
||||
email,
|
||||
password,
|
||||
logIn,
|
||||
getCalculator,
|
||||
getComponents,
|
||||
getSections,
|
||||
|
Reference in New Issue
Block a user