2 Commits

Author SHA1 Message Date
c18497b3fa remove pocketbase 2025-07-01 13:17:16 +02:00
bf317d2e34 button with image 2025-07-01 11:47:25 +02:00
74 changed files with 6846 additions and 13239 deletions

View File

@ -1,13 +0,0 @@
#!/bin/sh
command_name_bun="bun"
if ! command -v "$command_name_bun" &> /dev/null; then
echo "Command '$command_name_bun' not found. Installing..."
npm install -g "$command_name_bun"@latest
else
"$command_name_bun" lint || exit 1 && cd ..
fi

View File

@ -1,7 +1,5 @@
<template> <template>
<div <div class="main bg-bg-light text-text-light dark:text-text-dark dark:bg-bg-dark">
class="main bg-bg-light text-text-light dark:text-text-dark dark:bg-bg-dark"
>
<UApp> <UApp>
<NuxtLayout> <NuxtLayout>
<NuxtPage /> <NuxtPage />
@ -9,7 +7,6 @@
</UApp> </UApp>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const userStore = useUserStore() const userStore = useUserStore()

View File

@ -1,11 +1,11 @@
@import '@nuxt/ui'; @import "@nuxt/ui";
@import 'tailwindcss'; @import "tailwindcss";
@import '@iconscout/unicons/css/line.css'; @import '@iconscout/unicons/css/line.css';
@theme { @theme {
/* fonts */ /* fonts */
--font-inter: 'Inter', sans-serif; --font-inter: "Inter", sans-serif;
--font-bounded: 'Bounded', sans-serif; --font-bounded: "Bounded", sans-serif;
/* colors */ /* colors */
--color-bg-light: #fffefb; --color-bg-light: #fffefb;
@ -16,12 +16,12 @@
--color-text-light: #1a1a1a; --color-text-light: #1a1a1a;
--color-text-dark: #fffefb; --color-text-dark: #fffefb;
--color-block: #e8e7e0; --color-block: #E8E7E0;
--color-accent-green-light: #004f3d; --color-accent-green-light: #004F3D;
--color-accent-green-dark: #008567; --color-accent-green-dark: #008567;
--color-gray: #6b6b6b; --color-gray: #6B6B6B;
/* button */ /* button */
--color-button: #9a7f62; --color-button: #9a7f62;
--color-button-hover: #b7946d; --color-button-hover: #b7946d;
@ -54,19 +54,19 @@
} }
.space-25-55-75 { .space-25-55-75 {
@apply space-y-[25px] sm:space-y-[55px] md:space-y-[75px]; @apply space-y-[25px] sm:space-y-[55px] md:space-y-[75px]
} }
.space-25-55 { .space-25-55 {
@apply space-y-[25px] sm:space-y-[55px]; @apply space-y-[25px] sm:space-y-[55px]
} }
.space-25-75 { .space-25-75 {
@apply space-y-[25px] sm:space-y-[75px]; @apply space-y-[25px] sm:space-y-[75px]
} }
.space-55-75 { .space-55-75 {
@apply space-y-[55px] sm:space-y-[75px]; @apply space-y-[55px] sm:space-y-[75px]
} }
} }

461
bun.lock Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,25 @@
<template> <template>
<div ref="dropdownRef"> <div ref="dropdownRef">
<div class="relative cursor-pointer" @click="openCart = !openCart"> <div @click="openCart = !openCart" class="relative cursor-pointer">
<i class="uil uil-shopping-cart text-[31px]"></i> <i class="uil uil-shopping-cart text-[31px]"></i>
<div v-if="checkoutStore.products && checkoutStore.products.length > 0" <div v-if="productStore.cart.cart_items && productStore.cart.cart_items.length > 0"
class="w-[15px] h-[15px] rounded-full bg-accent-green-light dark:bg-accent-green-light text-white flex items-center justify-center text-[9px] absolute top-1 right-0"> class="w-[15px] h-[15px] rounded-full bg-accent-green-light dark:bg-accent-green-light text-white flex items-center justify-center text-[9px] absolute top-1 right-0">
{{ checkoutStore.products.length }} {{ productStore.cart.cart_items.length }}</div>
</div> </div>
</div> <div v-if="openCart" @click.self="openCart = !openCart"
<div v-if="openCart" class="absolute left-1/2 transform -translate-x-1/2 w-full px-4 sm:max-w-[768px] sm:px-[17px] md:max-w-[1000px] md:px-6 xl:max-w-[1920px] xl:px-20 right-0 z-50 flex items-center justify-end top-[100px] sm:top-[100px] md:top-[140px]">
class="absolute left-1/2 transform -translate-x-1/2 w-full px-4 sm:max-w-[768px] sm:px-[17px] md:max-w-[1000px] md:px-6 xl:max-w-[1920px] xl:px-20 right-0 z-50 flex items-center justify-end top-[100px] sm:top-[100px] md:top-[140px]"
@click.self="openCart = !openCart">
<div class="xl:w-[55%] md:w-[90%] w-full px-4 md:px-0"> <div class="xl:w-[55%] md:w-[90%] w-full px-4 md:px-0">
<div v-if="checkoutStore.products && checkoutStore.products.length > 0" <div v-if="productStore.cart.cart_items && productStore.cart.cart_items.length > 0"
class="w-full p-[25px] sm:p-[50px] bg-bg-light dark:bg-bg-dark border border-button rounded-xl sm:rounded-[32px] h-full space-25-55"> class="w-full p-[25px] sm:p-[50px] bg-bg-light dark:bg-bg-dark border border-button rounded-xl sm:rounded-[32px] h-full space-25-55">
<div> <div>
<div v-for="(item, index) in checkoutStore.products" :key="index" <!-- product -->
<div v-for="item in productStore.cart.cart_items"
class="py-[13px] sm:py-[25px] first:pt-0 border-b border-block"> class="py-[13px] sm:py-[25px] first:pt-0 border-b border-block">
<div class="flex items-center h-[100px] sm:h-[205px]"> <div class="flex items-center h-[100px] sm:h-[205px]">
<div class="min-w-[100px] sm:min-w-[205px] flex items-center justify-center h-[100px] sm:h-[205px]"> <div
<img :src="`/api/public/file/${item.picture_uuid}.webp`" alt="" class="min-w-[100px] sm:min-w-[205px] flex items-center justify-center h-[100px] sm:h-[205px]">
class="max-w-full max-h-full object-contain"> <img :src="`https://www.yourgold.cz/api/public/file/${item.picture_uuid}.webp`"
alt="" class="max-w-full max-h-full object-contain">
</div> </div>
<div class="flex flex-col justify-between min-h-full w-full gap-[7px] sm:gap-[15px]"> <div class="flex flex-col justify-between min-h-full w-full gap-[7px] sm:gap-[15px]">
@ -28,13 +28,13 @@
class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] max-w-[100px] sm:max-w-[200px] md:max-w-[250px]"> class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] max-w-[100px] sm:max-w-[200px] md:max-w-[250px]">
{{ item.name }} {{ item.name }}
</h3> </h3>
<i class="uil uil-trash-alt text-lg sm:text-2xl cursor-pointer" <i @click="productStore.deleteCartItem(item.cart_item_id)"
@click="productStore.deleteCartItem(item.cart_item_id)"></i> class="uil uil-trash-alt text-lg sm:text-2xl cursor-pointer"></i>
</div> </div>
<div class="flex flex-col gap-[10px]"> <div class="flex flex-col gap-[10px]">
<p <p
class="text-accent-green-light dark:text-accent-green-dark font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold"> class="text-accent-green-light dark:text-accent-green-dark font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold">
{{ menuStore.formatPrice(Number(item.total_price)) }} {{ item.total_price }}
</p> </p>
<div class="flex items-center gap-[2px] sm:gap-2 text-xl"> <div class="flex items-center gap-[2px] sm:gap-2 text-xl">
<div <div
@ -58,35 +58,27 @@
</div> </div>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h4 class="font-inter text-[12px] leading-[150%] font-bold uppercase sm:text-[24px]"> <h4 class="font-inter text-[12px] leading-[150%] font-bold uppercase sm:text-[24px]">{{
{{ $t('total_amount') }}</h4>
$t('total_amount') }}
</h4>
<p <p
class="text-accent-green-light dark:text-accent-green-dark font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold"> class="text-accent-green-light dark:text-accent-green-dark font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold">
{{ menuStore.formatPrice(Number(checkoutStore.fullProductsPrice)) }} {{ productStore.cart.total_value }}
</p> </p>
</div> </div>
<UiButtonArrow class="w-full" type="fill" :arrow="true" :full="true" @click="() => { <UiButtonArrow @click="() => {
if (userStore.isLogged) {
menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 12)) menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 12))
}
else {
menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 11))
}
openCart = false openCart = false
}"> }" class="w-full" type="fill" :arrow="true" :full="true">{{
{{ userStore.isLogged $t('to_checkout') }}
? $t('to_checkout') : $t('login') }}
</UiButtonArrow> </UiButtonArrow>
</div> </div>
<div v-else <div v-else
class="w-full p-[25px] sm:p-[50px] bg-bg-light dark:bg-bg-dark border border-button rounded-xl sm:rounded-[32px] flex items-center justify-center"> class="w-full p-[25px] sm:p-[50px] bg-bg-light dark:bg-bg-dark border border-button rounded-xl sm:rounded-[32px] flex items-center justify-center">
<div <div
class="border border-block inline-flex items-center justify-center h-[140px] sm:h-[200px] text-center rounded-[8px]"> class="border border-block inline-flex items-center justify-center h-[140px] sm:h-[200px] text-center rounded-[8px]">
<h4 class="font-inter text-base leading-[150%] font-bold uppercase sm:text-[20px] px-4 sm:px-10 md:text-xl"> <h4
{{ $t('empty_cart') }} class="font-inter text-base leading-[150%] font-bold uppercase sm:text-[20px] px-4 sm:px-10 md:text-xl">
</h4> {{ $t('empty_cart') }}</h4>
</div> </div>
</div> </div>
</div> </div>
@ -95,17 +87,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from "@vueuse/core";
const productStore = useProductStore() const productStore = useProductStore()
const checkoutStore = useCheckoutStore() const openCart = ref(false);
const openCart = ref(false)
const menuStore = useMenuStore() const menuStore = useMenuStore()
const userStore = useUserStore()
const dropdownRef = ref(null) const dropdownRef = ref(null);
onClickOutside(dropdownRef, () => { onClickOutside(dropdownRef, () => {
openCart.value = false openCart.value = false
}) });
</script> </script>

View File

@ -1,64 +1,31 @@
<template> <template>
<div ref="dropdownRef" <div class="flex flex-col gap-2 relative" ref="dropdownRef">
class="flex flex-col gap-2 relative" <UButton color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-down"
>
<UButton
color="neutral"
variant="subtle"
trailing-icon="i-lucide-chevron-down"
class="bg-bg-light dark:bg-bg-dark m-0 ring-0 text-xl font-medium uppercase cursor-pointer hover:bg-inherit text-text-light dark:text-text-dark" class="bg-bg-light dark:bg-bg-dark m-0 ring-0 text-xl font-medium uppercase cursor-pointer hover:bg-inherit text-text-light dark:text-text-dark"
block block @click="isOpen = !isOpen">
@click="isOpen = !isOpen" {{ $session.cookieData.value.country.iso_code }}/{{ $session.cookieData.value.currency.iso_code }}
>
{{ $session.cookieData.value.country.iso_code }}/{{
$session.cookieData.value.currency.iso_code
}}
</UButton> </UButton>
<div <div class="absolute ring-0 top-12 p-0 m-0 border-none w-48 z-50" v-if="isOpen">
v-if="isOpen" <div class="border border-button px-4 py-[10px] rounded-[5px] bg-bg-light dark:bg-bg-dark">
class="absolute ring-0 top-12 p-0 m-0 border-none w-48 z-50"
>
<div
class="border border-button px-4 py-[10px] rounded-[5px] bg-bg-light dark:bg-bg-dark"
>
<div class="p-0 flex flex-col gap-4 bg-bg-light dark:bg-bg-dark"> <div class="p-0 flex flex-col gap-4 bg-bg-light dark:bg-bg-dark">
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<p>{{ $t("country") }}</p> <p>{{ $t("country") }}</p>
<div <div
class="w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 space-y-1" class="w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 space-y-1">
> <div class="p-0" @click="openDrop('country')">
<div class="p-0" <div class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
@click="openDrop('country')" {{ $session.currentCountryIso }} <span> <i
> class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
<div
class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark"
>
{{ $session.currentCountryIso }}
<span>
<i
class="uil uil-angle-down text-2xl font-light cursor-pointer"
/></span>
</div> </div>
</div> </div>
<div <div v-if="dropCountry"
v-if="dropCountry" class="bg-bg-light dark:bg-bg-dark 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="bg-bg-light dark:bg-bg-dark 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"> <div class="overflow-auto h-[200px] w-full">
<p <p @click="() => { $session.setCountry(item.iso_code); $session.loadSession(); dropCountry = false }"
v-for="item in menuStore.countries"
:key="item.iso_code"
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 hover:bg-block dark:hover:bg-button pl-2 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
@click=" v-for="item in menuStore.countries" :key="item.iso_code">
() => {
$session.setCountry(item.iso_code);
$session.loadSession();
dropCountry = false;
}
"
>
{{ item?.name }} {{ item?.name }}
</p> </p>
</div> </div>
@ -68,39 +35,20 @@
<div class="flex flex-col items-start gap-[6px]"> <div class="flex flex-col items-start gap-[6px]">
<p>{{ $t("currency") }}</p> <p>{{ $t("currency") }}</p>
<div <div
class="w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 space-y-1" class="w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 space-y-1">
> <div class="p-0" @click="openDrop()">
<div class="p-0" <div class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
@click="openDrop()" {{ $session.currentCurrencyIso }}<span> <i
> class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
<div
class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark"
>
{{ $session.currentCurrencyIso
}}<span>
<i
class="uil uil-angle-down text-2xl font-light cursor-pointer"
/></span>
</div> </div>
</div> </div>
<div <div v-if="dropCurrency"
v-if="dropCurrency" class="bg-bg-light dark:bg-bg-dark 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="bg-bg-light dark:bg-bg-dark 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 w-full"> <div class="overflow-auto w-full">
<p <p @click="() => { $session.setCurrency(item.iso_code); $session.loadSession(); dropCurrency = false }"
v-for="item in menuStore.currencies"
:key="item.iso_code"
class="w-full hover:bg-block dark:hover:bg-button pl-1 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]" class="w-full hover:bg-block dark:hover:bg-button pl-1 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
@click=" v-for="item in menuStore.currencies" :key="item.iso_code">
() => {
$session.setCurrency(item.iso_code);
$session.loadSession();
dropCurrency = false;
}
"
>
{{ item?.name }} {{ item?.name }}
</p> </p>
</div> </div>
@ -114,14 +62,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from "@vueuse/core";
const {$session} = useNuxtApp();
const { $session } = useNuxtApp() const isOpen = ref(false);
const menuStore = useMenuStore();
const isOpen = ref(false) const dropdownRef = ref(null);
const menuStore = useMenuStore()
const dropdownRef = ref(null)
const dropCountry = ref(false) const dropCountry = ref(false)
const dropCurrency = ref(false) const dropCurrency = ref(false)
@ -130,16 +77,16 @@ function openDrop(type?: string) {
if (type === 'country') { if (type === 'country') {
dropCurrency.value = false dropCurrency.value = false
dropCountry.value = !dropCountry.value dropCountry.value = !dropCountry.value
} } else {
else {
dropCountry.value = false dropCountry.value = false
dropCurrency.value = !dropCurrency.value dropCurrency.value = !dropCurrency.value
} }
} }
onClickOutside(dropdownRef, () => { onClickOutside(dropdownRef, () => {
isOpen.value = false isOpen.value = false
dropCurrency.value = false dropCurrency.value = false
dropCountry.value = false dropCountry.value = false
}) });
</script> </script>

View File

@ -4,41 +4,27 @@
<div class="w-full border-b border-border"> <div class="w-full border-b border-border">
<UiContainer class="relative"> <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 <ul class="flex items-center justify-between whitespace-nowrap w-full">
class="flex items-center justify-between gap-5 whitespace-nowrap w-full" <li v-for="(item, index) in menuStore.menu" @click="menuStore.navigateToItem(item)" :key="item.id"
> :class="['hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer text-lg transition-all text-inter',
<li route.params.id == item.id.toString() ? 'text-accent-green-light dark:text-accent-green-dark font-bold underline' : false]">
v-for="(item, index) in menuStore.menu" 0{{ index + 1 }} <br />
:key="item.id"
:class="[
'hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer text-lg transition-all text-inter',
route.params.id == item.id.toString()
? 'text-accent-green-light dark:text-accent-green-dark font-bold underline'
: false,
]"
@click="menuStore.navigateToItem(item)"
>
0{{ index + 1 }} <br>
{{ item.front_menu_lang[0].name }} {{ item.front_menu_lang[0].name }}
</li> </li>
</ul> </ul>
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<img <img class="cursor-pointer" :src="isDark ? '/logo-dark.svg' : '/logo.svg'" alt="logo"
class="cursor-pointer" @click="menuStore.navigateToItem()" />
:src="isDark ? '/logo-dark.svg' : '/logo.svg'"
alt="logo"
@click="menuStore.navigateToItem()"
>
</ClientOnly> </ClientOnly>
<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]">
<div> <div>
<i v-if="!userStore.isLogged" <i v-if="!userStore.isLogged"
data-testid="login-icon" @click="menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 11))"
class="uil uil-user text-[31px] cursor-pointer" class="uil uil-user text-[31px] cursor-pointer"></i>
@click="menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 11))"></i>
<div v-else class="py-[6px] px-3 border border-block rounded-sm"> <div v-else class="py-[6px] px-3 border border-block rounded-sm">
{{ userStore.user }} <!-- {{ userStore.user }} -->
Arina Yakovenko
</div> </div>
</div> </div>
<CartPopup /> <CartPopup />
@ -48,16 +34,13 @@
<CountryCurrencySelector /> <CountryCurrencySelector />
</div> </div>
<ThemeSwitcher /> <ThemeSwitcher />
<button <button @click="menuStore.navigateToShop" :class="[
:class="[ 'cursor-pointer transition-all text-inter',
'cursor-pointer transition-all text-inter whitespace-nowrap',
route.params.id == '5' route.params.id == '5'
? 'text-accent-green-light dark:text-accent-green-dark font-bold pb-1 border-b-2' ? 'text-accent-green-light dark:text-accent-green-dark font-bold pb-1 border-b-2'
: 'hover:bg-button-hover bg-button text-white font-medium rounded-xl px-6 py-3', : 'hover:bg-button-hover bg-button text-white font-medium rounded-xl px-6 py-3'
]" ]">
@click="menuStore.navigateToShop" {{ $t('eshop') }}
>
{{ $t("eshop") }}
</button> </button>
</div> </div>
</div> </div>
@ -65,38 +48,16 @@
</div> </div>
<!-- md --> <!-- md -->
<div <div class="hidden w-full md:flex md:flex-col xl:hidden items-center justify-center">
class="hidden w-full md:flex md:flex-col xl:hidden items-center justify-center"
>
<div class="w-full border-border border-b"> <div class="w-full border-border border-b">
<UiContainer class="h-[116px] flex items-center justify-between"> <UiContainer class="h-[116px] flex items-center justify-between">
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<img <img class="cursor-pointer" :src="isDark ? '/logo-dark.svg' : '/logo.svg'" alt="logo"
class="cursor-pointer" @click="menuStore.navigateToItem()" />
:src="isDark ? '/logo-dark.svg' : '/logo.svg'"
alt="logo"
@click="menuStore.navigateToItem()"
>
</ClientOnly> </ClientOnly>
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<div class="flex items-center gap-[30px]"> <div class="flex items-center gap-[30px]">
<div> <i class="uil uil-user text-[31px] cursor-pointer"></i>
<i
v-if="!userStore.isLogged"
class="uil uil-user text-[31px] cursor-pointer"
@click="
menuStore.navigateToItem(
menuStore.menuItems?.find((item) => item.id === 11),
)
"
/>
<div
v-else
class="py-[6px] px-3 border border-block rounded-sm"
>
{{ userStore.user }}
</div>
</div>
<CartPopup /> <CartPopup />
</div> </div>
<div class="flex"> <div class="flex">
@ -104,54 +65,26 @@
<CountryCurrencySelector /> <CountryCurrencySelector />
</div> </div>
<ThemeSwitcher /> <ThemeSwitcher />
<i <i variant="subtle" block class="uil uil-apps text-[33px] cursor-pointer" @click="open = !open"></i>
variant="subtle"
block
class="uil uil-apps text-[33px] cursor-pointer"
@click="open = !open"
/>
</div> </div>
</UiContainer> </UiContainer>
</div> </div>
<UCollapsible <UCollapsible :ui="{ content: 'w-full' }" v-model:open="open" class="w-full">
v-model:open="open"
:ui="{ content: 'w-full' }"
class="w-full"
>
<template #content> <template #content>
<div class="w-full border-border border-b pt-6 pb-8"> <div class="w-full border-border border-b pt-6 pb-8">
<UiContainer class="flex flex-col gap-[30px]"> <UiContainer class="flex flex-col gap-[30px]">
<div <div v-for="(item, index) in menuStore.menu" :key="index"
v-for="(item, index) in menuStore.menu" :class="['flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer', route.params.slug === item.front_menu_lang[0].link_rewrite && 'text-accent-green-light dark:text-accent-green-dark font-bold underline']"
:key="index" @click="() => { menuStore.navigateToItem(item); open = false; }">
:class="[
'flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer',
route.params.slug === item.front_menu_lang[0].link_rewrite
&& 'text-accent-green-light dark:text-accent-green-dark font-bold underline',
]"
@click="
() => {
menuStore.navigateToItem(item);
open = false;
}
"
>
<div class="leading-[70%] text-inter"> <div class="leading-[70%] text-inter">
<span class="mr-4">0{{ index + 1 }}</span> <span class="mr-4">0{{ index + 1 }}</span>
{{ item.front_menu_lang[0].name }} {{ item.front_menu_lang[0].name }}
</div> </div>
<!-- <i class="uil uil-arrow-up-right text-[35px]"></i> --> <!-- <i class="uil uil-arrow-up-right text-[35px]"></i> -->
<svg <svg width="20" height="20" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
width="20"
height="20"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z" d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z"
fill="currentColor" fill="currentColor" />
/>
</svg> </svg>
</div> </div>
</UiContainer> </UiContainer>
@ -161,88 +94,42 @@
</div> </div>
<!-- sm --> <!-- sm -->
<div <div class="hidden w-full items-center justify-between sm:flex sm:flex-col md:hidden">
class="hidden w-full items-center justify-between sm:flex sm:flex-col md:hidden"
>
<div class="w-full border-border border-b"> <div class="w-full border-border border-b">
<UiContainer class="h-[84px] flex items-center justify-between"> <UiContainer class="h-[84px] flex items-center justify-between">
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<img <img class="cursor-pointer" :src="isDark ? '/logo-dark.svg' : '/logo.svg'" alt="logo"
class="cursor-pointer" @click="menuStore.navigateToItem()" />
:src="isDark ? '/logo-dark.svg' : '/logo.svg'"
alt="logo"
@click="menuStore.navigateToItem()"
>
</ClientOnly> </ClientOnly>
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<div class="flex items-center gap-[30px]"> <div class="flex items-center gap-[30px]">
<div> <i class="uil uil-user text-[31px] cursor-pointer"></i>
<i
v-if="!userStore.isLogged"
class="uil uil-user text-[31px] cursor-pointer"
@click="
menuStore.navigateToItem(
menuStore.menuItems?.find((item) => item.id === 11),
)
"
/>
<div
v-else
class="py-[6px] px-3 border border-block rounded-sm"
>
{{ userStore.user }}
</div>
</div>
<CartPopup /> <CartPopup />
</div> </div>
<i <i variant="subtle" block class="uil uil-apps text-[30px] cursor-pointer" @click="open = !open"></i>
variant="subtle"
block
class="uil uil-apps text-[30px] cursor-pointer"
@click="open = !open"
/>
</div> </div>
</UiContainer> </UiContainer>
</div> </div>
<UCollapsible <UCollapsible :ui="{ content: 'w-full' }" v-model:open="open" class="w-full">
v-model:open="open"
:ui="{ content: 'w-full' }"
class="w-full"
>
<template #content> <template #content>
<div class="w-full border-border border-b pt-6 pb-8"> <div class="w-full border-border border-b pt-6 pb-8">
<UiContainer class="flex flex-col gap-[30px]"> <UiContainer class="flex flex-col gap-[30px]">
<div <div v-for="(item, index) in menuStore.menu" @click="
v-for="(item, index) in menuStore.menu"
:key="index"
:class="[
'flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer',
route.params.slug === item.front_menu_lang[0].link_rewrite
&& 'text-accent-green-light dark:text-accent-green-dark font-bold underline',
]"
@click="
() => { () => {
menuStore.navigateToItem(item); menuStore.navigateToItem(item);
open = false; open = false;
} }
" " :key="index"
> :class="['flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer', route.params.slug === item.front_menu_lang[0].link_rewrite && 'text-accent-green-light dark:text-accent-green-dark font-bold underline']">
<div class="leading-[70%] text-inter"> <div class="leading-[70%] text-inter">
<span class="mr-4">0{{ index + 1 }}</span> <span class="mr-4">0{{ index + 1 }}</span>
{{ item.front_menu_lang[0].name }} {{ item.front_menu_lang[0].name }}
</div> </div>
<!-- <i class="uil uil-arrow-up-right text-[35px]"></i> --> <!-- <i class="uil uil-arrow-up-right text-[35px]"></i> -->
<svg <svg width="20" height="20" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
width="20"
height="20"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z" d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z"
fill="currentColor" fill="currentColor" />
/>
</svg> </svg>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
@ -274,74 +161,35 @@
<div class="w-full border-border border-b"> <div class="w-full border-border border-b">
<UiContainer class="h-[84px] flex items-center justify-between"> <UiContainer class="h-[84px] flex items-center justify-between">
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<img <img class="cursor-pointer" :src="isDark ? '/logo-dark.svg' : '/logo.svg'" alt="logo"
class="cursor-pointer" @click="menuStore.navigateToItem()" />
:src="isDark ? '/logo-dark.svg' : '/logo.svg'"
alt="logo"
@click="menuStore.navigateToItem()"
>
</ClientOnly> </ClientOnly>
<div class="flex items-center gap-6"> <div class="flex items-center gap-6">
<div>
<i
class="uil uil-user text-[30px] cursor-pointer"
@click="
!userStore.isLogged
&& menuStore.navigateToItem(
menuStore.menuItems?.find((item) => item.id === 11),
)
"
/>
</div>
<CartPopup /> <CartPopup />
<i <i variant="subtle" block class="uil uil-apps text-[30px] cursor-pointer" @click="open = !open"></i>
variant="subtle"
block
class="uil uil-apps text-[30px] cursor-pointer"
@click="open = !open"
/>
</div> </div>
</UiContainer> </UiContainer>
</div> </div>
<UCollapsible <UCollapsible :ui="{ content: 'w-full' }" v-model:open="open" class="w-full">
v-model:open="open"
:ui="{ content: 'w-full' }"
class="w-full"
>
<template #content> <template #content>
<div class="w-full border-border border-b pt-6 pb-8"> <div class="w-full border-border border-b pt-6 pb-8">
<UiContainer class="flex flex-col gap-[30px]"> <UiContainer class="flex flex-col gap-[30px]">
<div <div v-for="(item, index) in menuStore.menu" @click="
v-for="(item, index) in menuStore.menu"
:key="index"
:class="[
'flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer',
route.params.slug === item.front_menu_lang[0].link_rewrite
&& 'text-accent-green-light dark:text-accent-green-dark font-bold underline',
]"
@click="
() => { () => {
menuStore.navigateToItem(item); menuStore.navigateToItem(item);
open = false; open = false;
} }
" " :key="index"
> :class="['flex items-center justify-between transition-all hover:text-accent-green-light dark:hover:text-accent-green-dark cursor-pointer', route.params.slug === item.front_menu_lang[0].link_rewrite && 'text-accent-green-light dark:text-accent-green-dark font-bold underline']">
<div class="leading-[70%] text-inter"> <div class="leading-[70%] text-inter">
<span class="mr-4">0{{ index + 1 }}</span> <span class="mr-4">0{{ index + 1 }}</span>
{{ item.front_menu_lang[0].name }} {{ item.front_menu_lang[0].name }}
</div> </div>
<!-- <i class="uil uil-arrow-up-right text-[35px]"></i> --> <!-- <i class="uil uil-arrow-up-right text-[35px]"></i> -->
<svg <svg width="20" height="20" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
width="20"
height="20"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z" d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z"
fill="currentColor" fill="currentColor" />
/>
</svg> </svg>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
@ -369,27 +217,26 @@
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import CartPopup from './CartPopup.vue' 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 userStore = useUserStore() const menuStore = useMenuStore();
const open = ref(false) const userStore = useUserStore();
const colorMode = useColorMode() const productStore = useProductStore();
const menuStore = useMenuStore() const open = ref(false);
const checkoutStore = useCheckoutStore() const colorMode = useColorMode();
await checkoutStore.getUserCart() productStore.getCart()
const route = useRoute() const route = useRoute()
const isDark = computed({ const isDark = computed({
get() { get() {
return colorMode.value === 'dark' return colorMode.value === "dark";
}, },
set(_isDark) { set(_isDark) {
colorMode.preference = _isDark ? 'dark' : 'light' colorMode.preference = _isDark ? "dark" : "light";
}, },
}) });
</script> </script>

View File

@ -34,20 +34,14 @@
/> />
</defs> </defs>
</svg> --> </svg> -->
<img class="image" <img class="image" src="/block.webp" alt="">
src="/block.webp"
alt=""
>
<slot /> <slot />
</div> </div>
</template> </template>
<script lang="ts" setup></script> <script lang="ts" setup></script>
<style> <style>
.image { .image {
clip-path: path( clip-path: path("M0 20C0 8.9543 8.95431 0 20 0H847.666C858.641 0 867.565 8.84318 867.665 19.817L869.815 254.817C869.917 265.934 860.933 275 849.816 275H653C641.954 275 633 283.954 633 295V330C633 341.046 624.046 350 613 350H20C8.95431 350 0 341.046 0 330V20Z");
'M0 20C0 8.9543 8.95431 0 20 0H847.666C858.641 0 867.565 8.84318 867.665 19.817L869.815 254.817C869.917 265.934 860.933 275 849.816 275H653C641.954 275 633 283.954 633 295V330C633 341.046 624.046 350 613 350H20C8.95431 350 0 341.046 0 330V20Z'
);
} }
</style> </style>

View File

@ -14,11 +14,7 @@
group: 'px-[5px] py-[10px]', group: 'px-[5px] py-[10px]',
item: 'hover:bg-block dark:hover:bg-button rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50', item: 'hover:bg-block dark:hover:bg-button rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50',
}" }"
@update:model-value=" @update:model-value="setLocale($event); $session.setLanguage($event); $session.loadSession()"
setLocale($event);
$session.setLanguage($event);
$session.loadSession();
"
> >
<template #leading="{ modelValue }"> <template #leading="{ modelValue }">
<div class="flex items-center gap-2 text-xl font-medium uppercase"> <div class="flex items-center gap-2 text-xl font-medium uppercase">
@ -43,8 +39,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const { locale, locales, setLocale } = useI18n() const { locale, locales, setLocale } = useI18n();
const { $session } = useNuxtApp() const {$session} = useNuxtApp();
const selectedLocaleCode = ref(locale.value);
const selectedLocaleCode = ref(locale.value)
</script> </script>

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,12 @@
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<div class="flex h-8 w-8 cursor-pointer items-center justify-center"> <div class="flex h-8 w-8 cursor-pointer items-center justify-center">
<i <i
@click="isDark = !isDark"
:class="[ :class="[
'uil text-[32px] cursor-pointer', 'uil text-[32px] cursor-pointer',
isDark ? 'uil-moon' : 'uil-sun', isDark ? 'uil-moon' : 'uil-sun',
]" ]"
@click="isDark = !isDark" ></i>
/>
</div> </div>
<template #fallback> <template #fallback>
<div class="size-20" /> <div class="size-20" />
@ -16,14 +16,14 @@
</template> </template>
<script setup> <script setup>
const colorMode = useColorMode() const colorMode = useColorMode();
const isDark = computed({ const isDark = computed({
get() { get() {
return colorMode.value === 'dark' return colorMode.value === "dark";
}, },
set(_isDark) { set(_isDark) {
colorMode.preference = _isDark ? 'dark' : 'light' colorMode.preference = _isDark ? "dark" : "light";
}, },
}) });
</script> </script>

View File

@ -2,35 +2,21 @@
<UiContainer class="space-y-[55px] sm:space-y-[75px] md:space-y-[100px]"> <UiContainer class="space-y-[55px] sm:space-y-[75px] md:space-y-[100px]">
<div class="space-25-55-75"> <div class="space-25-55-75">
<div class="grid sm:grid-cols-7 md:grid-cols-4 xl:grid-cols-2"> <div class="grid sm:grid-cols-7 md:grid-cols-4 xl:grid-cols-2">
<h2 <h2 class="sm:col-start-2 sm:col-end-8 md:col-end-5 xl:col-start-2 h2-bold-bounded">
class="sm:col-start-2 sm:col-end-8 md:col-end-5 xl:col-start-2 h2-bold-bounded"
>
{{ component.front_section_lang[0].data.section_1.title }} {{ component.front_section_lang[0].data.section_1.title }}
</h2> </h2>
</div> </div>
<div class="space-y-[40px] sm:space-55-75"> <div class="space-y-[40px] sm:space-55-75">
<div <div v-for="(item, index) in component.front_section_lang[0].data.section_1.text_blocks" :key="index"
v-for="(item, index) in component.front_section_lang[0].data.section_1 class="grid grid-cols-7 md:grid-cols-4 xl:grid-cols-2">
.text_blocks"
:key="index"
class="grid grid-cols-7 md:grid-cols-4 xl:grid-cols-2"
>
<div class="flex gap-[200px]"> <div class="flex gap-[200px]">
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">0{{ index + 1 }}</h4>
0{{ index + 1 }} <h4 class="hidden xl:block h4-uppercase-bold-inter w-full whitespace-nowrap">
</h4>
<h4
class="hidden xl:block h4-uppercase-bold-inter w-full whitespace-nowrap"
>
{{ item.block_title }} {{ item.block_title }}
</h4> </h4>
</div> </div>
<div <div class="col-start-2 col-end-8 md:col-end-5 space-y-[25px] sm:space-y-[45px] md:space-y-[55px]">
class="col-start-2 col-end-8 md:col-end-5 space-y-[25px] sm:space-y-[45px] md:space-y-[55px]" <h4 class="xl:hidden h4-uppercase-bold-inter w-full whitespace-nowrap">
>
<h4
class="xl:hidden h4-uppercase-bold-inter w-full whitespace-nowrap"
>
{{ item.block_title }} {{ item.block_title }}
</h4> </h4>
<p>{{ item.block_text }}</p> <p>{{ item.block_text }}</p>
@ -42,44 +28,27 @@
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ component.front_section_lang[0].data.section_2.title }} {{ component.front_section_lang[0].data.section_2.title }}
</h2> </h2>
<div <div class="grid sm:grid-cols-2 xl:grid-cols-4 gap-[30px] auto-cols-fr auto-rows-fr">
class="grid sm:grid-cols-2 xl:grid-cols-4 gap-[30px] auto-cols-fr auto-rows-fr" <div class="border border-border rounded-2xl p-8 flex flex-col justify-between gap-[55px]"
> v-for="(item, index) in component.front_section_lang[0].data.section_2.text_blocks" :key="index">
<div
v-for="(item, index) in component.front_section_lang[0].data.section_2
.text_blocks"
:key="index"
class="border border-border rounded-2xl p-8 flex flex-col justify-between gap-[55px]"
>
<div> <div>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">0{{ index + 1 }}</h4>
0{{ index + 1 }} <h4 class="h4-uppercase-bold-inter">{{ item.block_title }}</h4>
</h4>
<h4 class="h4-uppercase-bold-inter">
{{ item.block_title }}
</h4>
</div> </div>
<p>{{ item.block_text }}</p> <p>{{ item.block_text }}</p>
</div> </div>
<div <div
class="row-end-4 sm:row-end-3 col-start-1 col-end-2 xl:row-end-1 xl:col-start-3 xl:col-end-4 flex flex-col gap-[10px] md:gap-[30px]" class="row-end-4 sm:row-end-3 col-start-1 col-end-2 xl:row-end-1 xl:col-start-3 xl:col-end-4 flex flex-col gap-[10px] md:gap-[30px]">
> <div class="w-full h-full md:h-[211px] rounded-2xl" :style="{
<div
class="w-full h-full md:h-[211px] rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <div class="w-full h-full md:h-[211px] rounded-2xl" :style="{
<div
class="w-full h-full md:h-[211px] rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -88,35 +57,25 @@
{{ component.front_section_lang[0].data.section_3.title }} {{ component.front_section_lang[0].data.section_3.title }}
</h2> </h2>
<div class="flex flex-col space-25-55"> <div class="flex flex-col space-25-55">
<p <p v-for="(item, index) in component.front_section_lang[0].data.section_3.text_blocks" :key="index">
v-for="(item, index) in component.front_section_lang[0].data.section_3
.text_blocks"
:key="index"
>
{{ item }} {{ item }}
</p> </p>
</div> </div>
</div> </div>
<div class="grid xl:space-25-55 grid-cols-1 xl:grid-cols-2 xl:gap-[30px]"> <div class="grid xl:space-25-55 grid-cols-1 xl:grid-cols-2 xl:gap-[30px]">
<div <div class="flex flex-col gap-[25px] sm:gap-[55px] md:gap-[75px] xl:justify-between">
class="flex flex-col gap-[25px] sm:gap-[55px] md:gap-[75px] xl:justify-between"
>
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ component.front_section_lang[0].data.section_4.title }} {{ component.front_section_lang[0].data.section_4.title }}
</h2> </h2>
<p>{{ component.front_section_lang[0].data.section_4.description }}</p> <p>{{ component.front_section_lang[0].data.section_4.description }}</p>
</div> </div>
<div <div class="mb-[25px] mt-5 md:mb-[55px] xl:m-0 w-full h-[222px] sm:h-[371px] rounded-2xl" :style="{
class="mb-[25px] mt-5 md:mb-[55px] xl:m-0 w-full h-[222px] sm:h-[371px] rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <p class="xl:col-start-2 xl:col-end-3">{{ component.front_section_lang[0].data.section_4.description_second
<p class="xl:col-start-2 xl:col-end-3"> }}</p>
{{ component.front_section_lang[0].data.section_4.description_second }}
</p>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
@ -133,36 +92,37 @@ defineProps<{
front_section_lang: { front_section_lang: {
data: { data: {
section_1: { section_1: {
title: string title: string;
text_blocks: [ text_blocks: [
{ {
block_title: string block_title: string;
block_text: string block_text: string;
},
]
} }
];
};
section_2: { section_2: {
title: string title: string;
text_blocks: [ text_blocks: [
{ {
block_title: string block_title: string;
block_text: string block_text: string;
},
]
} }
];
};
section_3: { section_3: {
title: string title: string;
text_blocks: [] text_blocks: [];
} };
section_4: { section_4: {
title: string title: string;
description: string description: string;
description_second: string description_second: string;
} };
} }
id_front_section: number id_front_section: number
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -2,61 +2,77 @@
<UiContainer class="space-y-[55px] sm:space-y-[75px] md:space-y-[100px]"> <UiContainer class="space-y-[55px] sm:space-y-[75px] md:space-y-[100px]">
<div class="grid grid-cols-1 xl:grid-cols-2 gap-[10px] sm:gap-[30px]"> <div class="grid grid-cols-1 xl:grid-cols-2 gap-[10px] sm:gap-[30px]">
<div class="flex flex-col gap-[10px] sm:gap-[30px]"> <div class="flex flex-col gap-[10px] sm:gap-[30px]">
<div class="h-[240px] sm:h-[400px] md:h-[490px] bg-block p-[25px] sm:p-[50px] rounded-2xl flex flex-col justify-between"> <div
class="h-[240px] sm:h-[400px] md:h-[490px] bg-block p-[25px] sm:p-[50px] rounded-2xl flex flex-col justify-between">
<div> <div>
<h1 class="h1-big text-accent-green-light mb-6 md:mb-10"> <h1 class="h1-big text-accent-green-light mb-6 md:mb-10">
{{ component.front_section_lang[0].data.title }} {{ component.front_section_lang[0].data.title }}
</h1> </h1>
<p class="text-text-light sm:text-xl md:text-2xl font-medium"> <p class="text-text-light sm:text-xl md:text-2xl font-medium">{{
{{ component.front_section_lang[0].data.description }} component.front_section_lang[0].data.description }}</p>
</p>
</div> </div>
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getShopMenu()?.id, slug: menuStore.getShopMenu()?.front_menu_lang.at(0)?.link_rewrite } }">
<UiButtonArrow type="fill" :arrow="true"> <UiButtonArrow type="fill" :arrow="true">
{{ $t("buy_gold") }} {{ $t('buy_gold') }}
</UiButtonArrow> </UiButtonArrow>
</nuxt-link>
</div> </div>
<div class="flex gap-[10px] sm:gap-[30px]"> <div class="flex gap-[10px] sm:gap-[30px]">
<div class="rounded-2xl h-[155px] sm:h-[225px] md:h-[280px] w-full" :style="{ backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundSize: 'cover', backgroundPosition: 'center' }" /> <div class="rounded-2xl h-[155px] sm:h-[225px] md:h-[280px] w-full" :style="{
<div class="rounded-2xl h-[155px] sm:h-[225px] md:h-[280px] w-full" :style="{ backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`, backgroundSize: 'cover' }" /> backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
<div backgroundSize: 'cover',
class="hidden sm:block xl:hidden h-[225px] md:h-[280px] rounded-2xl w-full border border-block" :style="{ backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundSize: 'cover', backgroundPosition: 'center' }" /> backgroundPosition: 'center',
}" />
<div class="rounded-2xl h-[155px] sm:h-[225px] md:h-[280px] w-full" :style="{
backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
}" />
<div class="hidden sm:block xl:hidden h-[225px] md:h-[280px] rounded-2xl w-full border border-block"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
}" />
</div> </div>
</div> </div>
<div class="sm:hidden xl:block h-[326px] md:h-auto min-w-[40%] xl:min-w-[60%] rounded-2xl border border-block" :style="{ backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundSize: 'cover', backgroundPosition: 'center' }" /> <div class="sm:hidden xl:block h-[326px] md:h-auto min-w-[40%] xl:min-w-[60%] rounded-2xl border border-block"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
}" />
</div> </div>
<div class="grid md:grid-cols-1 xl:grid-cols-2 gap-[20px] md:auto-rows-fr sm:gap-[30px]"> <div class="grid md:grid-cols-1 xl:grid-cols-2 gap-[20px] md:auto-rows-fr sm:gap-[30px]">
<div class="space-25-55-75"> <div class="space-25-55-75">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ component.front_section_lang[0].data.section_2.title }}</h2>
{{ component.front_section_lang[0].data.section_2.title }}
</h2>
<div class="space-y-[40px] sm:space-y-[50px]"> <div class="space-y-[40px] sm:space-y-[50px]">
<p v-for="(item, index) in component.front_section_lang[0].data .section_2.text_blocks" :key="index"> <p v-for="(item, index) in component.front_section_lang[0].data.section_2.text_blocks" :key="index">
{{ item }} {{ item }}
</p> </p>
</div> </div>
</div> </div>
<div class="w-full md:h-full rounded-2xl h-[327px] sm:h-[550px]" :style="{ backgroundImage: `url('/api/public/file/${component.img[3]}_l.webp')`, backgroundSize: 'cover', backgroundPosition: 'center' }" /> <div class="w-full md:h-full rounded-2xl h-[327px] sm:h-[550px]" :style="{
</div> backgroundImage: `url('/api/public/file/${component.img[3]}_l.webp')`,
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-y-[25px] sm:gap-y-[55px] xl:gap-y-0 gap-x-[30px]"> backgroundSize: 'cover',
<h2 class="h2-bold-bounded sm:col-start-1 sm:col-end-3"> backgroundPosition: 'center',
{{ component.front_section_lang[0].data.section_3.title }} }" />
</h2>
<div v-for="(item, index) in component.front_section_lang[0].data.section_3 .text_blocks" :key="index" class="flex flex-col justify-between space-25-55">
<p class="md:px-4 xl:p-0">
{{ item }}
</p>
<div class="rounded-2xl h-[227px] sm:h-[281px]" :style="{ backgroundImage: `url('/api/public/file/${component.img[index + 4]}_l.webp')`, backgroundSize: 'cover', backgroundPosition: 'center' }" />
</div> </div>
<div <div
class="sm:col-start-1 sm:col-end-3 xl:col-start-3 xl:col-end-5 flex justify-center xl:items-end xl:justify-end" class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-y-[25px] sm:gap-y-[55px] xl:gap-y-0 gap-x-[30px]">
> <h2 class="h2-bold-bounded sm:col-start-1 sm:col-end-3">{{
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getShopMenu()?.id, slug: menuStore.getShopMenu()?.front_menu_lang.at(0)?.link_rewrite } }"> component.front_section_lang[0].data.section_3.title }}
<UiButtonArrow type="fill" :arrow="true"> </h2>
{{ $t("buy_gold") }} <div v-for="(item, index) in component.front_section_lang[0].data.section_3.text_blocks" :key="index"
</UiButtonArrow> class="flex flex-col justify-between space-25-55">
</nuxt-link> <p class="md:px-4 xl:p-0">{{ item }}</p>
<div class=" rounded-2xl h-[227px] sm:h-[281px]" :style="{
backgroundImage: `url('/api/public/file/${component.img[index + 4]}_l.webp')`,
backgroundSize: 'cover',
backgroundPosition: 'center',
}" />
</div>
<div
class="sm:col-start-1 sm:col-end-3 xl:col-start-3 xl:col-end-5 flex justify-center xl:items-end xl:justify-end">
<UiButtonArrow type="fill" :arrow="true">{{ $t('buy_gold') }}</UiButtonArrow>
</div> </div>
</div> </div>
</UiContainer> </UiContainer>
@ -88,7 +104,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const menuStore = useMenuStore()
</script> </script>

View File

@ -8,50 +8,36 @@
<p>{{ component.front_section_lang[0].data.description }}</p> <p>{{ component.front_section_lang[0].data.description }}</p>
</div> </div>
</div> </div>
<div <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 grid-rows-5 md:grid-rows-3 xl:grid-rows-2 gap-8">
class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 grid-rows-5 md:grid-rows-3 xl:grid-rows-2 gap-8" <div v-for="(item, index) in component.front_section_lang[0].data.feature_blocks" :key="index" :class="[
>
<div
v-for="(item, index) in component.front_section_lang[0].data
.feature_blocks"
:key="index"
:class="[
'p-8 rounded-2xl border border-block flex flex-col justify-between gap-4', 'p-8 rounded-2xl border border-block flex flex-col justify-between gap-4',
index === 0 && 'bg-block dark:text-bg-dark', index === 0 && 'bg-block dark:text-bg-dark',
index === 1 && 'xl:col-start-2 xl:col-end-3', index === 1 && 'xl:col-start-2 xl:col-end-3',
index === 2 && 'xl:col-start-4 xl:col-end-5', index === 2 && 'xl:col-start-4 xl:col-end-5',
index === 3 index === 3 &&
&& 'md:col-start-1 md:col-end-3 xl:col-start-2 xl:col-end-4', 'md:col-start-1 md:col-end-3 xl:col-start-2 xl:col-end-4',
]" ]">
>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
<span>0{{ index + 1 }} <br></span>{{ item.block_title }} <span>0{{ index + 1 }} <br /></span>{{ item.block_title }}
</h4> </h4>
<p>{{ item.block_description }}</p> <p>{{ item.block_description }}</p>
</div> </div>
<div <div
class="sm:text-white xl:row-start-2 md:col-start-1 md:col-end-3 xl:col-end-2 w-full md:h-full flex justify-center xl:items-end" class="sm:text-white xl:row-start-2 md:col-start-1 md:col-end-3 xl:col-end-2 w-full md:h-full flex justify-center xl:items-end">
> <UiButtonArrow :arrow="true" type="fill">Zakoupit zlato</UiButtonArrow>
<UiButtonArrow :arrow="true"
type="fill"
>
Zakoupit zlato
</UiButtonArrow>
</div> </div>
<div <div class="rounded-2xl row-start-4 md:row-start-2 md:col-start-2 md:col-end-3 xl:col-start-4 xl:col-end-5"
class="rounded-2xl row-start-4 md:row-start-2 md:col-start-2 md:col-end-3 xl:col-start-4 xl:col-end-5"
:style="{ :style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'top', backgroundPosition: 'top',
}" }" />
/>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { UiButtonArrow } from '#components' import { UiButtonArrow } from "#components";
defineProps<{ defineProps<{
component: { component: {
@ -75,5 +61,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -5,33 +5,24 @@
{{ component.front_section_lang[0].data.section_1.title }} {{ component.front_section_lang[0].data.section_1.title }}
</h2> </h2>
<div class="flex flex-col xl:flex-row gap-12"> <div class="flex flex-col xl:flex-row gap-12">
<div <div class="rounded-2xl h-[255px] sm:h-[435px] md:h-[500px] w-full xl:min-w-[700px]" :style="{
class="rounded-2xl h-[255px] sm:h-[435px] md:h-[500px] w-full xl:min-w-[700px]"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'bottom', backgroundPosition: 'bottom',
}" }" />
/>
<div class="flex flex-col justify-between space-25-55"> <div class="flex flex-col justify-between space-25-55">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.section_1.sub_title }} {{ component.front_section_lang[0].data.section_1.sub_title }}
</h4> </h4>
<div class="flex flex-col"> <div class="flex flex-col">
<p <p v-for="(item, index) in component.front_section_lang[0].data.section_1.sub_description" :key="index">
v-for="(item, index) in component.front_section_lang[0].data
.section_1.sub_description"
:key="index"
>
{{ item }} {{ item }}
</p> </p>
</div> </div>
</div> </div>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ {{ component.front_section_lang[0].data.section_1.sub_title_second }}
component.front_section_lang[0].data.section_1.sub_title_second
}}
</h4> </h4>
</div> </div>
</div> </div>
@ -42,11 +33,7 @@
</h2> </h2>
<div class="space-25-55"> <div class="space-25-55">
<div class="flex flex-col w-full xl:w-[55%]"> <div class="flex flex-col w-full xl:w-[55%]">
<p <p v-for="(item, index) in component.front_section_lang[0].data.section_2.description" :key="index">
v-for="(item, index) in component.front_section_lang[0].data
.section_2.description"
:key="index"
>
{{ item }} {{ item }}
</p> </p>
</div> </div>
@ -58,54 +45,33 @@
</div> </div>
<div class="flex flex-col xl:flex-row gap-12"> <div class="flex flex-col xl:flex-row gap-12">
<div class="flex flex-col sm:flex-row gap-3 min-w-[60%]"> <div class="flex flex-col sm:flex-row gap-3 min-w-[60%]">
<div <div class="rounded-2xl h-[230px] sm:h-[300px] w-full xl:h-[770px]" :style="{
class="rounded-2xl h-[230px] sm:h-[300px] w-full xl:h-[770px]"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <div class="rounded-2xl xl:hidden w-full h-[230px] sm:h-[300px] xl:w-full xl:h-[328px]" :style="{
<div
class="rounded-2xl xl:hidden w-full h-[230px] sm:h-[300px] xl:w-full xl:h-[328px]"
:style="{
backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
<div class="flex flex-col justify-between space-25-55 xl:w-[70%]"> <div class="flex flex-col justify-between space-25-55 xl:w-[70%]">
<div <div class="hidden xl:block rounded-2xl max-full min-h-[330px]" :style="{
class="hidden xl:block rounded-2xl max-full min-h-[330px]"
:style="{
backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[2]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
<div class="h-full flex flex-col gap-5 xl:gap-0 justify-between"> <div class="h-full flex flex-col gap-5 xl:gap-0 justify-between">
<h4 class="col-start-2 col-end-3 h4-uppercase-bold-inter"> <h4 class="col-start-2 col-end-3 h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.section_4.title }} {{ component.front_section_lang[0].data.section_4.title }}
</h4> </h4>
<div class="flex flex-col"> <div class="flex flex-col">
<p <p v-if="component.front_section_lang[0].data.section_4.description"
v-if=" v-html="component.front_section_lang[0].data.section_4.description[0]"></p>
component.front_section_lang[0].data.section_4.description <br />
" <p v-if="component.front_section_lang[0].data.section_4.description"
v-html=" v-html="component.front_section_lang[0].data.section_4.description[1]"></p>
component.front_section_lang[0].data.section_4.description[0]
"
/>
<br>
<p
v-if="
component.front_section_lang[0].data.section_4.description
"
v-html="
component.front_section_lang[0].data.section_4.description[1]
"
/>
</div> </div>
<h4 class="col-start-2 col-end-3 h4-uppercase-bold-inter"> <h4 class="col-start-2 col-end-3 h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.section_5.title }} {{ component.front_section_lang[0].data.section_5.title }}
@ -153,5 +119,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -30,14 +30,14 @@
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}"></div> }" />
<div class="flex flex-col"> <div class="flex flex-col">
<div v-for="(item, index) in component.front_section_lang[0].data.story_description" :key="index"> <p v-for="(item, index) in component.front_section_lang[0].data.story_description" :key="index">
<p>{{ item }}</p> {{ item }}
<div v-if="index < component.front_section_lang[0].data.story_description.length - 1"> <div v-if="index < component.front_section_lang[0].data.story_description.length - 1">
<br> <br>
</div> </div>
</div> </p>
</div> </div>
</div> </div>
<div class="flex flex-col w-full gap-6"> <div class="flex flex-col w-full gap-6">
@ -45,12 +45,12 @@
{{ component.front_section_lang[0].data.story_subtitle }} {{ component.front_section_lang[0].data.story_subtitle }}
</h4> </h4>
<div class="flex flex-col justify-between xl:max-w-[70%]"> <div class="flex flex-col justify-between xl:max-w-[70%]">
<div v-for="(el, indexEl) in component.front_section_lang[0].data.story_sub_description" :key="indexEl"> <p v-for="(el, indexEl) in component.front_section_lang[0].data.story_sub_description" :key="indexEl">
<p>{{ el }}</p> {{ el }}
<div v-if="indexEl < component.front_section_lang[0].data.story_sub_description.length - 1"> <div v-if="indexEl < component.front_section_lang[0].data.story_sub_description.length - 1">
<br> <br>
</div> </div>
</div> </p>
</div> </div>
</div> </div>
</div> </div>
@ -58,6 +58,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ defineProps<{
component: { component: {
id: number id: number
@ -80,5 +81,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -2,32 +2,22 @@
<UiContainer class="space-y-[55px] md:space-y-[75px] xl:space-y-[100px]"> <UiContainer class="space-y-[55px] md:space-y-[75px] xl:space-y-[100px]">
<div class="space-y-10 sm:space-y-[55px] md:space-y-14"> <div class="space-y-10 sm:space-y-[55px] md:space-y-14">
<h1 class="h1 text-center"> <h1 class="h1 text-center">
<span <span v-for="(item, index) in component.front_section_lang[0].data.title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data.title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="index !== component.front_section_lang[0].data.title.length - 1">
v-if=" </span>
index !== component.front_section_lang[0].data.title.length - 1
"
/>
</span> </span>
</h1> </h1>
<div <div class="h-[180px] sm:h-[330px] md:h-[500px] rounded-2xl" :style="{
class="h-[180px] sm:h-[330px] md:h-[500px] rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
<div class="md:w-full xl:w-[70%] space-y-14"> <div class="md:w-full xl:w-[70%] space-y-14">
<p>{{ component.front_section_lang[0].data.main_description }}</p> <p>{{ component.front_section_lang[0].data.main_description }}</p>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
@ -39,61 +29,43 @@
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ component.front_section_lang[0].data.section_1_title }} {{ component.front_section_lang[0].data.section_1_title }}
</h2> </h2>
<p v-html="component.front_section_lang[0].data.section_1_description" /> <p v-html="component.front_section_lang[0].data.section_1_description"></p>
</div> </div>
<div class="grid space-25-75 xl:space-0 grid-cols-1 xl:grid-cols-2"> <div class="grid space-25-75 xl:space-0 grid-cols-1 xl:grid-cols-2">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
<span <span v-for="(item, index) in component.front_section_lang[0].data.section_2_title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data
.section_2_title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="index !== component.front_section_lang[0].data.title.length - 1">
v-if=" </span>
index !== component.front_section_lang[0].data.title.length - 1
"
/>
</span> </span>
</h2> </h2>
<p v-html="component.front_section_lang[0].data.section_2_description" /> <p v-html="component.front_section_lang[0].data.section_2_description"></p>
</div> </div>
<div class="space-25-75"> <div class="space-25-75">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ component.front_section_lang[0].data.section_3_title }} {{ component.front_section_lang[0].data.section_3_title }}
</h2> </h2>
<div <div class="grid grid-cols-1 space-25-55 md:space-0 xl:gap-2 md:grid-cols-2">
class="grid grid-cols-1 space-25-55 md:space-0 xl:gap-2 md:grid-cols-2"
>
<div class="space-y-[25px] sm:space-y-[45px]"> <div class="space-y-[25px] sm:space-y-[45px]">
<p> <p>{{ component.front_section_lang[0].data.section_3_description }}</p>
{{ component.front_section_lang[0].data.section_3_description }}
</p>
<div class=""> <div class="">
<p <p v-for="(item, index) in component.front_section_lang[0].data
v-for="(item, index) in component.front_section_lang[0].data .section_3_items" :key="index">
.section_3_items"
:key="index"
>
{{ index + 1 }}. {{ index + 1 }}.
{{ item }} {{ item }}
</p> </p>
</div> </div>
</div> </div>
<div <div class="h-[315px] xl:h-full rounded-2xl" :style="{
class="h-[315px] xl:h-full rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
</div> </div>
<div class="grid grid-cols-1 xl:grid-cols-2"> <div class="grid grid-cols-1 xl:grid-cols-2">
@ -101,9 +73,7 @@
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ component.front_section_lang[0].data.section_4_title }} {{ component.front_section_lang[0].data.section_4_title }}
</h2> </h2>
<p <p v-html="component.front_section_lang[0].data.section_4_description"></p>
v-html="component.front_section_lang[0].data.section_4_description"
/>
</div> </div>
</div> </div>
</UiContainer> </UiContainer>
@ -143,5 +113,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -2,59 +2,39 @@
<UiContainer class="space-55-75 xl:!space-y-[100px]"> <UiContainer class="space-55-75 xl:!space-y-[100px]">
<div class="space-25-75"> <div class="space-25-75">
<h2 class="h2-bold-bounded max-w-[95%]"> <h2 class="h2-bold-bounded max-w-[95%]">
<span <span v-for="(item, index) in component.front_section_lang[0].data.reasons_section_title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data
.reasons_section_title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="
v-if=" index !==
index component.front_section_lang[0].data.reasons_section_title.length - 1
!== component.front_section_lang[0].data.reasons_section_title ">
.length </span>
- 1
"
/>
</span> </span>
</h2> </h2>
<div <div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 md:px-10 xl:p-0 gap-8 auto-rows-fr">
class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 md:px-10 xl:p-0 gap-8 auto-rows-fr" <div v-for="(item, index) in component.front_section_lang[0].data.reason_blocks" :key="index" :class="[
>
<div
v-for="(item, index) in component.front_section_lang[0].data
.reason_blocks"
:key="index"
:class="[
'p-[25px] rounded-2xl border border-block flex flex-col justify-between gap-20', 'p-[25px] rounded-2xl border border-block flex flex-col justify-between gap-20',
index === 1 && 'xl:col-start-2 xl:col-end-3', index === 1 && 'xl:col-start-2 xl:col-end-3',
index === 2 && 'xl:col-start-4 xl:col-end-5', index === 2 && 'xl:col-start-4 xl:col-end-5',
index === 3 && 'xl:col-start-2 xl:col-end-3', index === 3 && 'xl:col-start-2 xl:col-end-3',
]" ]">
>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
<span>0{{ index + 1 }} <br></span>{{ item.title }} <span>0{{ index + 1 }} <br /></span>{{ item.title }}
</h4> </h4>
<p>{{ item.description }}</p> <p>{{ item.description }}</p>
</div> </div>
<div <div class="row-end-7 sm:row-auto rounded-2xl flex items-center justify-center min-h-[200px]">
class="row-end-7 sm:row-auto rounded-2xl flex items-center justify-center min-h-[200px]" <div class="w-full h-full rounded-2xl" :style="{
>
<div
class="w-full h-full rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'top', backgroundPosition: 'top',
}" }" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -66,39 +46,21 @@
</h2> </h2>
<div class="hidden xl:grid grid-cols-2 gap-6"> <div class="hidden xl:grid grid-cols-2 gap-6">
<div class="flex flex-col gap-20"> <div class="flex flex-col gap-20">
<p> <p>{{ component.front_section_lang[0].data.cta_description_intro }}</p>
{{ component.front_section_lang[0].data.cta_description_intro }} <p>{{ component.front_section_lang[0].data.cta_description_details }}</p>
</p>
<p>
{{
component.front_section_lang[0].data.cta_description_details
}}
</p>
</div> </div>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ {{ component.front_section_lang[0].data.main_call_to_action_statement }}
component.front_section_lang[0].data
.main_call_to_action_statement
}}
</h4> </h4>
</div> </div>
<div class="xl:hidden space-25-55"> <div class="xl:hidden space-25-55">
<div <div class="grid grid-cols-1 md:grid-cols-2 md:gap-[55px] space-25-55">
class="grid grid-cols-1 md:grid-cols-2 md:gap-[55px] space-25-55" <p>{{ component.front_section_lang[0].data.cta_description_intro }}</p>
>
<p>
{{ component.front_section_lang[0].data.cta_description_intro }}
</p>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ {{ component.front_section_lang[0].data.main_call_to_action_statement }}
component.front_section_lang[0].data
.main_call_to_action_statement
}}
</h4> </h4>
</div> </div>
<p> <p>{{ component.front_section_lang[0].data.cta_description_details }}</p>
{{ component.front_section_lang[0].data.cta_description_details }}
</p>
</div> </div>
</div> </div>
</div> </div>
@ -107,61 +69,41 @@
{{ component.front_section_lang[0].data.form_section_title }} {{ component.front_section_lang[0].data.form_section_title }}
</h4> </h4>
<div class="flex flex-col md:flex-row gap-8 md:gap-[30px] xl:gap-0"> <div class="flex flex-col md:flex-row gap-8 md:gap-[30px] xl:gap-0">
<div <div class="p-[25px] md:p-[50px] bg-block rounded-2xl space-y-[30px] xl:ml-40 xl:w-[65%]">
class="p-[25px] md:p-[50px] bg-block rounded-2xl space-y-[30px] xl:ml-40 xl:w-[65%]"
>
<div class="flex gap-[30px]"> <div class="flex gap-[30px]">
<input <input :placeholder="$t('first_name')" type="text"
:placeholder="$t('first_name')" class="border border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
type="text" <input :placeholder="$t('email')" type="text"
class="border border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" class="border border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
>
<input
:placeholder="$t('email')"
type="text"
class="border border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button"
>
</div> </div>
<textarea <textarea :placeholder="$t('form_question')"
:placeholder="$t('form_question')" class="border h-[145px] border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
class="border h-[145px] border-text-dark placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button"
/>
<div class="w-full flex justify-center sm:justify-start"> <div class="w-full flex justify-center sm:justify-start">
<UiButtonArrow type="border"> <UiButtonArrow type="border">{{
{{
$t("submit_form") $t("submit_form")
}} }}</UiButtonArrow>
</UiButtonArrow>
</div> </div>
</div> </div>
<div class="space-y-[30px] xl:px-[50px] sm:px-10"> <div class="space-y-[30px] xl:px-[50px] sm:px-10">
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ $t("contact_info") }} {{ $t('contact_info') }}
</h4> </h4>
<div <div class="flex flex-col sm:flex-row items-center sm:items-start md:flex-col justify-between gap-[30px]">
class="flex flex-col sm:flex-row items-center sm:items-start md:flex-col justify-between gap-[30px]"
>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("phone") }}</p>
{{ $t("phone") }}
</p>
<p>+420 608 428 782</p> <p>+420 608 428 782</p>
</div> </div>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("email") }}</p>
{{ $t("email") }}
</p>
<p>web@yourgold.cz</p> <p>web@yourgold.cz</p>
</div> </div>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("office_address") }}</p>
{{ $t("office_address") }}
</p>
<p> <p>
Floriána Nováka 3 <br> Floriána Nováka 3 <br />
796 01 Prostějov <br> 796 01 Prostějov <br />
Czech Republic <br> Czech Republic <br />
CZ 08435456 CZ 08435456
</p> </p>
</div> </div>
@ -170,43 +112,26 @@
</div> </div>
</div> </div>
<div class="flex flex-col xl:flex-row xl:h-[130px] gap-[45px]"> <div class="flex flex-col xl:flex-row xl:h-[130px] gap-[45px]">
<div <div class="w-full xl:w-[560px] h-[130px] xl:h-full rounded-2xl" :style="{
class="w-full xl:w-[560px] h-[130px] xl:h-full rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <div class="flex flex-col h-full justify-between items-stretch space-y-[25px] sm:space-y-[55px] xl:space-y-0">
<div <p>{{ component.front_section_lang[0].data.closing_inspirational_block }}</p>
class="flex flex-col h-full justify-between items-stretch space-y-[25px] sm:space-y-[55px] xl:space-y-0"
>
<p>
{{
component.front_section_lang[0].data.closing_inspirational_block
}}
</p>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
<span <span v-for="(item, index) in component.front_section_lang[0].data.title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data
.title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="
v-if=" index !==
index component.front_section_lang[0].data.reasons_section_title.length - 1
!== component.front_section_lang[0].data.reasons_section_title ">
.length </span>
- 1
"
/>
</span> </span>
</h4> </h4>
<p>{{ component.front_section_lang[0].data.final_tagline }}</p> <p>{{ component.front_section_lang[0].data.final_tagline }}</p>
@ -252,5 +177,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -1,43 +1,19 @@
<template> <template>
<div <div v-if="hasMainCategory" :class="['flex flex-col gap-[25px]', isOpen && 'border-b border-block pb-[10px]']">
v-if="hasMainCategory" <div @click="toggle" class="flex items-center justify-between rounded-lg cursor-pointer">
:class="[
'flex flex-col gap-[25px]',
isOpen && 'border-b border-block pb-[10px]',
]"
>
<div
class="flex items-center justify-between rounded-lg cursor-pointer"
@click="toggle"
>
<div class="flex items-center justify-between w-full"> <div class="flex items-center justify-between w-full">
<p <p class="text-lg xl:text-2xl font-extrabold text-black dark:text-white">
class="text-lg xl:text-2xl font-extrabold text-black dark:text-white"
>
{{ mainCategoryName }} {{ mainCategoryName }}
</p> </p>
<span <span :class="['flex items-center justify-center', isOpen && 'rotate-180', 'transition-all']"><i
:class="[ class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"></i></span>
'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"
/></span>
</div> </div>
</div> </div>
<ul v-show="isOpen" <ul class="flex flex-col gap-[25px]" v-show="isOpen">
class="flex flex-col gap-[25px]" <li @click="$emit('change-category', child)" v-for="child in subcategories" :key="child.id"
>
<li
v-for="child in subcategories"
:key="child.id"
class="text-base xl:text-lg cursor-pointer flex justify-between items-center" class="text-base xl:text-lg cursor-pointer flex justify-between items-center"
:class="child.id === props.active ? 'text-yellow' : ''" :class="child.id === props.active ? 'text-yellow' : ''">
@click="$emit('change-category', child)"
>
<span>{{ child.langs[0].Name }}</span> <span>{{ child.langs[0].Name }}</span>
<span>12</span> <span>12</span>
</li> </li>
@ -49,34 +25,22 @@
</template> </template>
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue';
// Define the props // Define the props
const props = defineProps({ const props = defineProps({
data: Array, data: Array,
active: Number, active: Number
}) });
defineEmits(['change-category']) const isOpen = ref(false);
const isOpen = ref(false)
const toggle = () => { const toggle = () => {
isOpen.value = !isOpen.value isOpen.value = !isOpen.value;
} };
// Computed properties // Computed properties
const hasMainCategory = computed( const hasMainCategory = computed(() => props.data && 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 : '');
props.data const subcategories = computed(() => hasMainCategory.value && props.data[0].children ? props.data[0].children : []);
&& 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> </script>

View File

@ -0,0 +1,6 @@
<template>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Modi perspiciatis adipisci quam odio natus odit excepturi
eveniet vitae. Fugit dicta officiis quos quia debitis perspiciatis porro ducimus earum placeat sunt?
</template>
<script lang="ts"></script>

View File

@ -1,78 +1,49 @@
<template> <template>
<UiContainer> <UiContainer>
<div class="xl:w-[85%] mx-auto"> <div class="w-[85%] mx-auto">
<div class="space-25-55"> <div v-if="userStore.isLogged" class="space-25-55">
<div class="w-full flex items-center sm:justify-center">
<div <div class="w-full flex items-center justify-center">
class="flex items-center justify-between sm:justify-center sm:gap-[25px] text-gray dark:text-button-disabled w-full sm:w-auto" <div class="flex justify-between">
> <div class="flex items-center gap-[25px] text-gray dark:text-button-disabled">
<div class="sm:px-6 sm:py-3 mx-auto"> <div class="px-6 py-3 mx-auto">
{{ $t("login") }} {{ $t("login") }}
</div> </div>
<div <div
class="cursor-pointer transition-all text-inter hover:bg-button-hover bg-button text-white font-medium rounded-xl px-3 py-1 sm:px-6 sm:py-3" class="cursor-pointer transition-all text-inter hover:bg-button-hover bg-button text-white font-medium rounded-xl px-6 py-3">
>
{{ $t("address") }} {{ $t("address") }}
</div> </div>
<div class="sm:px-6 sm:py-3 mx-auto"> <div class="px-6 py-3 mx-auto">
{{ $t("summary") }} {{ $t("summary") }}
</div> </div>
<div class="hidden sm:block sm:px-6 sm:py-3 sm:mx-auto"> <div class="px-6 py-3 mx-auto">
{{ $t("order_placed") }} {{ $t("order_placed") }}
</div> </div>
</div> </div>
</div> </div>
<div class="space-y-[25px] sm:space-y-[30px]"> </div>
<div class="space-y-[30px]">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ $t("Account address") }} {{ $t("Account address") }}
</h2> </h2>
<div class="flex flex-col gap-[30px] sm:flex-row"> <div class="flex flex-col gap-[30px] xl:flex-row">
<div class="flex flex-col sm:w-1/2 gap-[25px] sm:gap-[30px]"> <div class="flex flex-col w-1/2 gap-[30px]">
<CheckoutInput :id="1" <CheckoutInput v-model="checkoutStore.userName" :id="1" disabled>{{ $t("first_name")
v-model="checkoutStore.userName" }} </CheckoutInput>
disabled <CheckoutInput v-model="checkoutStore.lastName" :id="2" disabled>{{ $t("surname")
> }} </CheckoutInput>
{{ $t("first_name") }} <CheckoutInput v-model="checkoutStore.address" :id="3" disabled>{{ $t("address")
</CheckoutInput> }} </CheckoutInput>
<CheckoutInput :id="2" <CheckoutInput v-model="checkoutStore.postCode" :id="4" disabled>{{ $t("post_code")
v-model="checkoutStore.lastName" }} </CheckoutInput>
disabled
>
{{ $t("surname") }}
</CheckoutInput>
<CheckoutInput :id="3"
v-model="checkoutStore.address"
disabled
>
{{ $t("address") }}
</CheckoutInput>
<CheckoutInput :id="4"
v-model="checkoutStore.postCode"
disabled
>
{{ $t("post_code") }}
</CheckoutInput>
</div> </div>
<div class="flex flex-col sm:w-1/2 gap-[30px]"> <div class="flex flex-col w-1/2 gap-[30px]">
<CheckoutInput :id="5" <CheckoutInput v-model="checkoutStore.city" :id="5" disabled>{{ $t("city")
v-model="checkoutStore.city" }} </CheckoutInput>
disabled <CheckoutInput v-model="checkoutStore.country" :id="6" disabled>{{ $t("country")
> }} </CheckoutInput>
{{ $t("city") }} <CheckoutInput v-model="checkoutStore.accountPhoneNumber" :id="7" disabled>{{ $t("phone")
</CheckoutInput> }} </CheckoutInput>
<CheckoutInput :id="6"
v-model="checkoutStore.country"
disabled
>
{{ $t("country") }}
</CheckoutInput>
<CheckoutInput
:id="7"
v-model="checkoutStore.accountPhoneNumber"
disabled
>
{{ $t("phone") }}
</CheckoutInput>
</div> </div>
</div> </div>
</div> </div>
@ -80,311 +51,151 @@
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ $t("Shipping details") }} {{ $t("Shipping details") }}
</h2> </h2>
<div class="flex flex-col gap-2"> <div class="relative border border-block rounded-lg px-6 h-[67px] w-full">
<div <div class="flex items-center gap-[10px]">
:class="[ <div class="flex items-center gap-[25px]" v-if="!checkoutStore.vUseAccountPhoneNumber">
'flex items-center gap-[10px] relative border border-block rounded-lg h-[50px] sm:h-[67px] w-full',
checkoutStore.vUseAccountPhoneNumber && 'px-6',
]"
>
<div
v-if="!checkoutStore.vUseAccountPhoneNumber"
class="flex items-center gap-5 sm:gap-[25px]"
>
<div class="flex flex-col items-start gap-[25px]"> <div class="flex flex-col items-start gap-[25px]">
<div ref="dropdownIsoRef"
class="pl-[25px] relative w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0">
<div class="p-0" @click="dropIso = !dropIso">
<div <div
ref="dropdownIsoRef" class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
class="pl-5 sm:pl-[25px] relative w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0" {{ checkoutStore.selectedIso.name }} <span> <i
> class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
<div class="p-0"
@click="dropIso = !dropIso"
>
<div
class="flex items-center gap-2 text-base sm:text-xl font-medium uppercase text-text-light dark:text-text-dark"
>
<p class="hidden sm:block">
{{ checkoutStore.selectedIso.name }}
</p>
<p class="sm:hidden">
{{ checkoutStore.selectedIso.iso_code }}
</p>
<span>
<i
class="uil uil-angle-down text-2xl font-light cursor-pointer"
/></span>
</div> </div>
</div> </div>
<div <div v-if="dropIso"
v-if="dropIso" class="absolute w-full mt-2 left-0 bg-bg-light dark:bg-bg-dark rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 border border-button py-[10px] px-[5px]">
class="absolute w-[130px] sm:w-full mt-2 left-0 bg-bg-light dark:bg-bg-dark rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 ring-0 cursor-pointer 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="() => { checkoutStore.selectedIso = item; dropIso = false; checkoutStore.changePrefix(item.call_prefix as string) }"
<div
class="overflow-auto h-[200px] w-full overflow-x-hidden"
>
<p
v-for="item in menuStore.countries"
:key="item.iso_code"
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 hover:bg-block dark:hover:bg-button pl-2 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
@click=" v-for="item in menuStore.countries" :key="item.iso_code">
() => {
checkoutStore.selectedIso = item;
dropIso = false;
checkoutStore.changePrefix(
item.call_prefix as string,
);
}
"
>
{{ item?.name }} {{ item?.name }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<p class="text-sm sm:text-xl border-r pr-[10px] border-block"> <p class="text-xl">{{ checkoutStore.currentPrefix }}</p>
{{ checkoutStore.currentPrefix }}
</p>
</div> </div>
<input <input
id="phone" :value="checkoutStore.vUseAccountPhoneNumber ? checkoutStore.accountPhoneNumber : checkoutStore.phoneNumber"
:value=" :disabled="checkoutStore.vUseAccountPhoneNumber" @input="(e) => {
checkoutStore.vUseAccountPhoneNumber
? checkoutStore.accountPhoneNumber
: checkoutStore.phoneNumber
"
:disabled="checkoutStore.vUseAccountPhoneNumber"
type="tel"
placeholder="123 xxxx xxx"
class="placeholder:text-sm sm:placeholder:text-xl placeholder:text-gray placeholder:uppercase dark:placeholder:text-bg-light rounded-lg h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-0"
@input="
(e) => {
if (!checkoutStore.vUseAccountPhoneNumber) { if (!checkoutStore.vUseAccountPhoneNumber) {
checkoutStore.phoneNumber = ( checkoutStore.phoneNumber = (e.target as HTMLInputElement).value;
e.target as HTMLInputElement
).value;
} }
} }" type="tel" placeholder="123 xxxx xxx"
" class="placeholder:text-xl placeholder:text-gray placeholder:uppercase dark:placeholder:text-bg-light rounded-lg h-[67px] w-full focus:outline-none focus:ring-0 focus:border-0" />
>
</div> </div>
<p <p v-if="checkoutStore.phoneValidation === false && !checkoutStore.vUseAccountPhoneNumber"
v-if=" class="text-red-500">Invalid phone number</p>
checkoutStore.phoneValidation === false
&& !checkoutStore.vUseAccountPhoneNumber
"
class="text-red-500"
>
Invalid phone number
</p>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input <input @change="(event: Event) => {
id="checkbox" const target = event.target as HTMLInputElement
type="checkbox" target.checked ? checkoutStore.vUseAccountPhoneNumber = true : checkoutStore.vUseAccountPhoneNumber = false
class="border border-button !bg-inherit" checkoutStore.phoneValidation = null
@change=" }" type="checkbox" class="border border-button !bg-inherit" />
(event: Event) => { <p>{{ $t('use_account_phone') }}</p>
const target = event.target as HTMLInputElement;
target.checked
? (checkoutStore.vUseAccountPhoneNumber = true)
: (checkoutStore.vUseAccountPhoneNumber = false);
checkoutStore.phoneValidation = null;
}
"
>
<p>{{ $t("use_account_phone") }}</p>
</div> </div>
</div> </div>
<div class="space-y-[30px] h-full"> <div class="space-y-[30px]">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
{{ $t("Select delivery address") }} {{ $t("Select delivery address") }}
</h2> </h2>
<div class="flex items-center justify-center gap-[25px] h-[225px]">
<div class="w-[500px] flex flex-col gap-4 h-full">
<div v-for="(item, index) in checkoutStore.addressesList" :key="index"
:class="['flex h-full flex-col py-[15px] px-[25px] gap-[15px] rounded-lg border-2', checkoutStore.activeAddress === item ? 'border-button' : 'border-block']">
<div <div
class="flex flex-col md:flex-row items-center justify-center gap-[10px] sm:gap-[25px] md:h-[225px]" :class="['flex flex-col justify-between mt-1 h-full', checkoutStore.activeAddress !== item && 'text-gray dark:text-button-disabled']">
>
<div class="w-full sm:w-[500px] flex flex-col gap-4 h-full">
<div
v-for="(item, index) in checkoutStore.addressesList"
:key="index"
:class="[
'flex min-h-[200px] md:h-full flex-col py-[15px] px-[25px] gap-[15px] rounded-lg border-2',
checkoutStore.activeAddress === item
? 'border-button'
: 'border-block',
]"
>
<div
:class="[
'flex flex-col justify-between gap-[10px] h-full',
checkoutStore.activeAddress !== item
&& 'text-gray dark:text-button-disabled',
]"
>
<span>{{ item.address.name }} {{ item.address.surname }}</span> <span>{{ item.address.name }} {{ item.address.surname }}</span>
<span>{{ item.address.street }}</span> <span>{{ item.address.street }}</span>
<span>{{ item.address.postcode }} {{ item.address.city }}</span> <span>{{ item.address.postcode }} {{ item.address.city }}</span>
<span>{{ item.address.country_iso }}</span> <span>{{ item.address.country_iso }}</span>
</div> </div>
<div <div class="flex items-center gap-2 border-t pt-[15px] border-block">
class="flex items-center gap-2 border-t pt-[15px] border-block" <input :checked="checkoutStore.activeAddress ? true : false" @change="(event: Event) => {
> const target = event.target as HTMLInputElement
<input target.checked ? checkoutStore.activeAddress = item : checkoutStore.activeAddress = null;
id="checkbox"
:checked="checkoutStore.activeAddress ? true : false"
type="checkbox"
class="border border-button !bg-inherit"
@change="
(event: Event) => {
const target = event.target as HTMLInputElement;
target.checked
? (checkoutStore.activeAddress = item)
: (checkoutStore.activeAddress = null);
checkoutStore.isOpen = false; checkoutStore.isOpen = false;
} }" type="checkbox" class="border border-button !bg-inherit" />
" <p>{{ $t('choose_default_address') }}</p>
>
<p>{{ $t("choose_default_address") }}</p>
</div> </div>
</div> </div>
</div> </div>
<p class="uppercase"> <p class="uppercase">{{ $t("or") }}</p>
{{ $t("or") }} <div @click="() => {
</p> checkoutStore.isOpen = !checkoutStore.isOpen
<div checkoutStore.activeAddress = null
:class="[ }"
'cursor-pointer w-full sm:w-[500px] py-[15px] px-[25px] rounded-lg border-2 flex flex-col items-center justify-center min-h-[200px] md:h-full', :class="['cursor-pointer w-[500px] py-[15px] px-[25px] rounded-lg border-2 flex flex-col items-center justify-center h-full', checkoutStore.isOpen ? 'border-button text-button' : 'text-gray border-block ']">
checkoutStore.isOpen
? 'border-button text-button'
: 'text-gray border-block ',
]"
@click="
() => {
checkoutStore.isOpen = !checkoutStore.isOpen;
checkoutStore.activeAddress = null;
}
"
>
<h4 <h4
:class="[ :class="['font-inter text-base leading-[150%] uppercase text-[16px] sm:text-[20px] border-b', checkoutStore.isOpen ? 'border-button' : 'border-gray']">
'font-inter text-base leading-[150%] uppercase text-[16px] sm:text-[20px] border-b',
checkoutStore.isOpen ? 'border-button' : 'border-gray',
]"
>
{{ $t("add_new_address") }} {{ $t("add_new_address") }}
</h4> </h4>
</div> </div>
</div> </div>
<div <div v-if="checkoutStore.isOpen"
v-if="checkoutStore.isOpen" class="flex flex-col items-center gap-[30px] justify-center w-full">
class="flex flex-col items-center gap-[30px] justify-center w-full"
>
<div class="flex flex-col gap-[30px] xl:flex-row w-full"> <div class="flex flex-col gap-[30px] xl:flex-row w-full">
<div class="flex flex-col sm:w-1/2 gap-[25px] sm:gap-[30px]"> <div class="flex flex-col w-1/2 gap-[30px]">
<CheckoutInput <CheckoutInput v-model="checkoutStore.vNewAddressName" :placeholder="$t('first_name')"
:id="8" :id="8">{{ $t("first_name") }}
v-model="checkoutStore.vNewAddressName"
:placeholder="$t('first_name')"
>
{{ $t("first_name") }}
</CheckoutInput> </CheckoutInput>
<CheckoutInput <CheckoutInput v-model="checkoutStore.vNewAddressAddress" :placeholder="$t('address')"
:id="9" :id="9">{{ $t("address") }}
v-model="checkoutStore.vNewAddressAddress"
:placeholder="$t('address')"
>
{{ $t("address") }}
</CheckoutInput> </CheckoutInput>
<CheckoutInput <CheckoutInput v-model="checkoutStore.vNewAddressCity" :placeholder="$t('city')"
:id="10" :id="10">{{ $t("city") }}
v-model="checkoutStore.vNewAddressCity"
:placeholder="$t('city')"
>
{{ $t("city") }}
</CheckoutInput> </CheckoutInput>
</div> </div>
<div class="flex flex-col sm:w-1/2 gap-[25px] sm:gap-[30px]"> <div class="flex flex-col w-1/2 gap-[30px]">
<CheckoutInput <CheckoutInput v-model="checkoutStore.vNewAddressSurname" :placeholder="$t('surname')"
:id="11" :id="11">{{ $t("surname") }}
v-model="checkoutStore.vNewAddressSurname"
:placeholder="$t('surname')"
>
{{ $t("surname") }}
</CheckoutInput> </CheckoutInput>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">
{{ $t("country") }} {{ $t("country") }}
</p> </p>
<div ref="dropdownCountryRef"
class="relative w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0">
<div class="border border-block placeholder:text-gray dark:placeholder:text-button-disabled rounded-lg px-6 h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2 flex items-center justify-start"
@click="dropCountry = !dropCountry">
<div <div
ref="dropdownCountryRef" class="flex items-center gap-2 text-xl font-medium uppercase text-text-light dark:text-text-dark">
class="relative w-full ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0" {{ checkoutStore.vNewAddressCountry ?
> checkoutStore.vNewAddressCountry.name : '-' }} <span> <i
<div class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
class="border border-block placeholder:text-gray dark:placeholder:text-button-disabled rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2 flex items-center justify-start"
@click="dropCountry = !dropCountry"
>
<div
class="flex items-center gap-2 text-base sm:text-xl font-medium uppercase text-text-light dark:text-text-dark"
>
{{
checkoutStore.vNewAddressCountry
? checkoutStore.vNewAddressCountry.name
: "-"
}}
<span>
<i
class="uil uil-angle-down text-2xl font-light cursor-pointer"
/></span>
</div> </div>
</div> </div>
<div <div v-if="dropCountry"
v-if="dropCountry" class="absolute z-50 w-full mt-2 left-0 bg-bg-light dark:bg-bg-dark rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 border border-button py-[10px] px-[5px]">
class="absolute z-50 w-full mt-2 left-0 bg-bg-light dark:bg-bg-dark rounded-[5px] data-highlighted:not-data-disabled:before:bg-button/50 ring-0 cursor-pointer focus:ring-0 outline-none focus-visible:ring-0 border border-button py-[10px] px-[5px]"
>
<div class="overflow-auto h-[200px] w-full"> <div class="overflow-auto h-[200px] w-full">
<p <p @click="() => { checkoutStore.vNewAddressCountry = item; dropCountry = false }"
v-for="item in menuStore.countries"
:key="item.iso_code"
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 hover:bg-block dark:hover:bg-button pl-2 py-2 text-base text-text-light dark:text-text-dark rounded-[5px]"
@click=" v-for="item in menuStore.countries" :key="item.iso_code">
() => {
checkoutStore.vNewAddressCountry = item;
dropCountry = false;
}
"
>
{{ item?.name }} {{ item?.name }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<CheckoutInput <CheckoutInput v-model="checkoutStore.vNewAddressCode" :placeholder="$t('post_code')"
:id="13" :id="13">{{ $t("post_code") }}
v-model="checkoutStore.vNewAddressCode"
:placeholder="$t('post_code')"
>
{{ $t("post_code") }}
</CheckoutInput> </CheckoutInput>
</div> </div>
</div> </div>
<div> <div>
<span v-if="addressValidation === false" <span v-if="addressValidation === false" class="text-red"> {{
class="text-red" $t("Remember to select a shipping address") }}</span>
> <div class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap">
{{ $t("Remember to select a shipping address") }}</span> <button @click="checkoutStore.uploadAddress()"
<div :class="['h-[40px] cursor-pointer min-w-40 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']">
class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap"
>
<button
:class="[
'h-[40px] cursor-pointer min-w-40 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',
]"
@click="checkoutStore.uploadAddress()"
>
{{ $t("add_new_address") }} {{ $t("add_new_address") }}
</button> </button>
</div> </div>
@ -393,41 +204,18 @@
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<div <div class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap">
class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap" <button @click="checkoutStore.sendForm()" :disabled="!checkoutStore.activeAddress"
> :class="['h-[40px] cursor-pointer min-w-40 rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px]', checkoutStore.activeAddress ? 'bg-button text-text-dark group-hover:bg-button-hover' : ' bg-button-disabled text-gray']">
<button
:disabled="!checkoutStore.activeAddress"
:class="[
'h-[40px] cursor-pointer min-w-40 rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px]',
checkoutStore.activeAddress
? 'bg-button text-text-dark group-hover:bg-button-hover'
: ' bg-button-disabled text-gray',
]"
@click="checkoutStore.sendForm()"
>
{{ $t("continue") }} {{ $t("continue") }}
</button> </button>
<div <div
:class="[ :class="['flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px]', checkoutStore.activeAddress ? 'bg-button text-text-dark group-hover:bg-button-hover' : ' bg-button-disabled text-gray']">
'flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px]', <svg class="" width=" 26" height="26" viewBox="0 0 26 26" fill="none"
checkoutStore.activeAddress xmlns="http://www.w3.org/2000/svg">
? 'bg-button text-text-dark group-hover:bg-button-hover'
: ' bg-button-disabled text-gray',
]"
>
<svg
class=""
width=" 26"
height="26"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z" d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z"
fill="currentColor" fill="currentColor" />
/>
</svg> </svg>
</div> </div>
</div> </div>
@ -438,25 +226,27 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onClickOutside } from '@vueuse/core' import { LazyColorScheme } from '#components';
import CheckoutInput from '../ui/CheckoutInput.vue' import CheckoutInput from '../ui/CheckoutInput.vue';
import { onClickOutside } from "@vueuse/core";
const checkoutStore = useCheckoutStore() const checkoutStore = useCheckoutStore();
const userStore = useUserStore()
const menuStore = useMenuStore() const menuStore = useMenuStore()
const dropIso = ref(false) const dropIso = ref(false)
const dropCountry = ref(false) const dropCountry = ref(false)
const addressValidation = ref<null | boolean>(null) const addressValidation = ref<null | boolean>(null);
const dropdownIsoRef = ref(null) const dropdownIsoRef = ref(null);
const dropdownCountryRef = ref(null) const dropdownCountryRef = ref(null);
onClickOutside(dropdownIsoRef, () => { onClickOutside(dropdownIsoRef, () => {
dropIso.value = false dropIso.value = false
}) });
onClickOutside(dropdownCountryRef, () => { onClickOutside(dropdownCountryRef, () => {
dropCountry.value = false dropCountry.value = false
}) });
checkoutStore.getCheckout() checkoutStore.getCheckout()
checkoutStore.getAddressList() checkoutStore.getAddressList()

View File

@ -1,252 +0,0 @@
<template>
<UiContainer v-if="checkoutStore.modalMadeOrder">
<NuxtLink to="/" class="mt-6 text-blue-500 underline" @click="checkoutStore.modalMadeOrder = false">Go back home
</NuxtLink>
</UiContainer>
<UiContainer v-else>
<div class="xl:w-[85%] mx-auto space-25-55">
<div class="space-25-55">
<div class="w-full flex items-center sm:justify-center">
<div
class="flex items-center justify-between sm:justify-center sm:gap-[25px] text-gray dark:text-button-disabled w-full sm:w-auto">
<div class="sm:px-6 sm:py-3 mx-auto">
{{ $t("login") }}
</div>
<div class="sm:px-6 sm:py-3 mx-auto">
{{ $t("address") }}
</div>
<div
class="cursor-pointer transition-all text-inter hover:bg-button-hover bg-button text-white font-medium rounded-xl px-3 py-1 sm:px-6 sm:py-3">
{{ $t("summary") }}
</div>
<div class="hidden sm:block sm:px-6 sm:py-3 sm:mx-auto">
{{ $t("order_placed") }}
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-[30px]">
<div class="col-start-1 col-end-2 sm:col-end-3 space-y-5">
<h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.product_list }}
</h4>
<div class="border-2 border-block rounded-[15px] p-[25px] sm:p-[50px] space-25-55">
<div v-for="(item, index) in checkoutStore.products" :key="index">
<div class="flex items-center h-[100px] sm:h-[150px]">
<div class="min-w-[100px] sm:min-w-[150px] flex items-center justify-center h-[100px] sm:h-[150px]">
<img :src="`/api/public/file/${item.picture_uuid}.webp`" alt=""
class="max-w-full max-h-full object-contain">
</div>
<div class="flex flex-col justify-between min-h-full w-full gap-[7px] sm:gap-[15px]">
<div class="w-full flex items-center justify-between">
<h3
class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] max-w-[100px] sm:max-w-[200px] md:max-w-[250px]">
{{ item.name }}
</h3>
</div>
<div class="flex w-full justify-between gap-[10px]">
<p
class="text-accent-green-light dark:text-accent-green-dark font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold">
{{ item.total_price }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="col-start-1 col-end-2 sm:col-end-3 xl:col-auto flex flex-col sm:flex-row xl:flex-col items-center gap-[30px]">
<div class="space-y-5 w-full">
<h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.account_address }}
</h4>
<div
class="border-2 border-block rounded-[8px] px-[25px] py-[15px] flex flex-col gap-1 text-sm sm:text-lg md:text-xl">
<span>{{ checkoutStore.defaultAddress?.name }} {{
checkoutStore.defaultAddress?.surname }}</span>
<span>{{ checkoutStore.defaultAddress?.street }}</span>
<span>{{ checkoutStore.defaultAddress?.postcode }} {{
checkoutStore.defaultAddress?.city }}</span>
</div>
</div>
<div class="flex flex-col space-y-5 w-full h-full">
<h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.note }}
</h4>
<textarea id="1" v-model="checkoutStore.vNote"
class="border-2 border-block rounded-[8px] p-[15px] w-full flex-1 resize-none placeholder:text-button"
name="rty" :placeholder="component.front_section_lang[0].data.note"></textarea>
</div>
</div>
<div class="col-start-1 col-end-2 xl:col-end-3 space-y-5">
<h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.delivery_type }}
</h4>
<div
class="border-2 border-block rounded-[15px] p-[25px] sm:p-[50px] space-y-[25px] text-sm sm:text-lg md:text-xl">
<div v-for="(delivery, index) in checkoutStore.deliveryOption" :key="index"
class="flex flex-col cursor-pointer" @click="checkoutStore.setCurrentDelivery(delivery)">
<div class="flex items-end justify-between">
<div class="flex gap-[15px]">
<div class="mt-1 w-4 h-4 border-2 border-button rounded-full">
<div v-if="checkoutStore.currentDelivery === delivery"
class="w-full h-full border-3 border-bg-light dark:border-bg-dark rounded-full bg-button">
</div>
</div>
<div class="flex flex-col">
<span>{{ delivery.delivery_supplier_name }}</span>
<span>{{ delivery.country_name }}</span>
</div>
</div>
<p class="font-inter text-lg sm:text-[21px] md:text-2xl leading-[150%] font-bold">
{{ menuStore.formatPrice(Number(delivery.shippment_price)) }}
</p>
</div>
</div>
</div>
</div>
<div class="space-y-5 w-full flex flex-col h-full">
<h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.payment_method }}
</h4>
<div
class="border-2 border-block rounded-[8px] px-[25px] py-[25px] sm:py-[15px] flex-1 flex flex-col gap-1 text-sm sm:text-xl overflow-auto">
<UCarousel :prev-icon="'i-lucide-chevron-left'" :next-icon="'i-lucide-chevron-right'" :ui="{
viewport: 'h-full w-full',
container: 'h-full w-full',
item: 'h-full w-full pl-7',
prev: '-start-8 sm:-start-12 ring-0 text-button disable:text-block p-0 ring-inset ring-accented bg-inherit disabled:bg-inherit',
next: '-end-8 sm:-end-12 ring-0 text-button disable:text-block p-0 text-[30px] bg-inherit disabled:bg-inherit',
arrows: '',
}" :prev="{ size: 'xl' }" :next="{ size: 'xl' }" arrows :items="checkoutStore.paymentMethods"
class="relative sm:max-w-full mx-4 sm:mx-10 h-full">
<template #default="{ item }">
<div class="flex flex-col items-start justify-between h-full leading-[250%] sm:leading-[150%]">
<span>{{ item.bank_name }}</span>
<span>{{ item.country_name }}</span>
<span>{{ item.street_and_number }}</span>
<span>{{ item.iban }}</span>
<span>{{ item.swift }}</span>
</div>
</template>
</UCarousel>
</div>
</div>
</div>
<div class="w-full border-y-2 border-block p-[25px] flex flex-col gap-[15px]">
<div class="flex items-center justify-between">
<p class="text-sm sm:text-xl">
{{ component.front_section_lang[0].data.subtotal }}
</p>
<p class="text-lg sm:text-xl font-medium">
{{
menuStore.formatPrice(Number(checkoutStore.fullProductsPrice)) }}
</p>
</div>
<div class="flex items-center justify-between">
<p class="text-sm sm:text-xl">
{{ component.front_section_lang[0].data.shipping_cost }}
</p>
<p class="text-lg sm:text-xl font-medium">
{{
menuStore.formatPrice(Number(checkoutStore.shippingPrice)) }}
</p>
</div>
<div class="flex items-center justify-between uppercase text-lg sm:text-xl">
<p>{{ component.front_section_lang[0].data.total }}</p>
<p
class="text-accent-green-light dark:text-accent-green-dark font-inter text-xl sm:text-2xl leading-[150%] font-bold">
{{ menuStore.formatPrice(Number(checkoutStore.fullPrice)) }}
</p>
</div>
</div>
<div class="flex flex-col sm:flex-row items-center justify-between mt-1 gap-[55px]">
<div class="flex flex-col gap-1 text-white w-full">
<div class="flex gap-3 text-black dark:text-white items-center">
<input id="first" v-model="checkoutStore.vLegal" type="checkbox" name="first" />
<!-- <label for="first" class="text-sm leading-snug">
{{ $t("I accept ") }}
<NuxtLink target="_blank"
:to="{ name: 'lang-info-name', params: { lang: menuStore.selectedLanguage.iso_code, name: 'legal_statement.html' } }"
class="underline cursor-pointer">
{{ $t("the legal statement*") }}
</NuxtLink>
</label> -->
<p class="text-sm sm:text-lg">
{{ $t("I accept ") }} {{ $t("the legal statement*") }}
</p>
</div>
<span v-if="checkoutStore.legalValidation" class="text-xs text-red-600 ml-6">
{{ $t("You need to accept this") }}
</span>
<div class="flex gap-3 text-black dark:text-white items-center mt-2">
<input id="second" v-model="checkoutStore.vTerms" type="checkbox" name="second" />
<!-- <label for="second" class="text-sm leading-snug">
{{ $t("I accept ") }}
<NuxtLink target="_blank"
:to="{ name: 'lang-info-name', params: { lang: menuStore.selectedLanguage.iso_code, name: 'general_terms_and_conditions.html' } }"
class="underline cursor-pointer">
{{ $t("general terms and conditions*") }}
</NuxtLink>
</label> -->
<p class="text-sm sm:text-lg">
{{ $t("I accept ") }} {{ $t("general terms and conditions*") }}
</p>
</div>
<span v-if="checkoutStore.termsValidation" class="text-xs text-red-600 ml-6">
{{ $t("You need to accept this") }}
</span>
</div>
<UiButtonArrow type="fill" :arrow="true" @click="checkoutStore.sendSummaryForm">
{{ $t("Buy") }}
</UiButtonArrow>
</div>
</div>
</UiContainer>
</template>
<script setup lang="ts">
import { UCarousel } from '#components'
const checkoutStore = useCheckoutStore()
const menuStore = useMenuStore()
await checkoutStore.getOrder()
await checkoutStore.getBankAccount()
await checkoutStore.getUserCart()
await checkoutStore.getDeliveryOptions()
await checkoutStore.getDefAddress()
defineProps<{
component: {
id: number
name: string
img: string[]
component_name: string
is_no_lang: boolean
page_name: string
front_section_lang: {
data: {
product_list: string
account_address: string
note: string
delivery_type: string
payment_method: string
subtotal: string
shipping_cost: string
total: string
accept: string
legal: string
terms: string
}
id_front_section: number
id_lang: number
}[]
}
}>()
</script>

View File

@ -1,65 +1,46 @@
<template> <template>
<UiContainer class="space-y-[45px]"> <UiContainer class="space-y-[45px]">
<div class="xl:w-[70%] space-y-[25px]"> <div class="xl:w-[70%] space-y-[25px]">
<h1 class="h1"> <h1 class="h1">{{ component.front_section_lang[0].data.title }}</h1>
{{ component.front_section_lang[0].data.title }} <p v-html="component.front_section_lang[0].data.description"></p>
</h1>
<p v-html="component.front_section_lang[0].data.description" />
</div> </div>
<div class="flex flex-col md:flex-row gap-8 md:gap-[30px] xl:gap-0"> <div class="flex flex-col md:flex-row gap-8 md:gap-[30px] xl:gap-0">
<div <div class="p-[25px] md:p-[50px] bg-block rounded-2xl space-y-[30px] xl:ml-40 xl:w-[65%]">
class="p-[25px] md:p-[50px] bg-block rounded-2xl space-y-[30px] xl:ml-40 xl:w-[65%]"
>
<div class="flex gap-[30px]"> <div class="flex gap-[30px]">
<input <input :placeholder="$t('first_name')" type="text"
:placeholder="$t('first_name')" class="border border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
type="text" <input :placeholder="$t('email')" type="text"
class="border border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" class="border border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
>
<input
:placeholder="$t('email')"
type="text"
class="border border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button"
>
</div> </div>
<textarea <textarea :placeholder="$t('form_question')"
:placeholder="$t('form_question')" class="border h-[276px] border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button" />
class="border h-[276px] border-button placeholder:text-button rounded-lg px-3 py-1.5 w-full focus:outline-none focus:ring-0 focus:border-2 text-button"
/>
<div class="w-full flex justify-center sm:justify-start"> <div class="w-full flex justify-center sm:justify-start">
<UiButtonArrow type="border"> <UiButtonArrow type="border">{{
{{ $t("submit_form") }} $t("submit_form")
</UiButtonArrow> }}</UiButtonArrow>
</div> </div>
</div> </div>
<div class="space-y-[30px] xl:px-[50px] sm:px-10"> <div class="space-y-[30px] xl:px-[50px] sm:px-10">
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ $t("contact_info") }} {{ $t('contact_info') }}
</h4> </h4>
<div <div
class="flex flex-col sm:flex-row items-center sm:items-start md:flex-col justify-between gap-[15px] sm:gap-[30px]" class="flex flex-col sm:flex-row items-center sm:items-start md:flex-col justify-between gap-[15px] sm:gap-[30px]">
>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("phone") }}</p>
{{ $t("phone") }}
</p>
<p>+420 608 428 782</p> <p>+420 608 428 782</p>
</div> </div>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("email") }}</p>
{{ $t("email") }}
</p>
<p>web@yourgold.cz</p> <p>web@yourgold.cz</p>
</div> </div>
<div> <div>
<p class="text-gray"> <p class="text-gray">{{ $t("office_address") }}</p>
{{ $t("office_address") }}
</p>
<p> <p>
Floriána Nováka 3 <br> Floriána Nováka 3 <br />
796 01 Prostějov <br> 796 01 Prostějov <br />
Czech Republic <br> Czech Republic <br />
CZ 08435456 CZ 08435456
</p> </p>
</div> </div>
@ -68,7 +49,6 @@
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ defineProps<{
component: { component: {
@ -80,12 +60,13 @@ defineProps<{
page_name: string page_name: string
front_section_lang: { front_section_lang: {
data: { data: {
title: string title: string;
description: string description: string;
} }
id_front_section: number id_front_section: number
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -1,26 +1,32 @@
<template> <template>
<div class="flex gap-24"> <div class="flex gap-24">
<div class="price-container"> <div class="price-container">
<div class="slider" <div class="slider" v-html="duplicatedContent">
v-html="duplicatedContent"
/> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const res 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>"
= '<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 productStore = useProductStore()
const activeElement = ref(1);
productStore.getModules() productStore.getModules()
// Computed property to duplicate the content // Computed property to duplicate the content
const duplicatedContent = computed(() => { const duplicatedContent = computed(() => {
const originalContent = res || '' const originalContent = res || '';
return originalContent + originalContent + originalContent + originalContent return originalContent + originalContent + originalContent + originalContent;
}) });
const changeActive = (item: number) => {
activeElement.value = item;
};
</script> </script>
<style scoped> <style scoped>
.price-container { .price-container {
/* display: flex; */ /* display: flex; */

View File

@ -2,84 +2,58 @@
<div class="border-t border-border pt-[75px]"> <div class="border-t border-border pt-[75px]">
<UiContainer class="flex flex-col gap-24"> <UiContainer class="flex flex-col gap-24">
<div <div
class="grid grid-cols-1 md:grid-cols-2 gap-[75px] xl:gap-0 xl:grid-cols-none xl:grid-flow-col auto-cols-max justify-between" class="grid grid-cols-1 md:grid-cols-2 gap-[75px] xl:gap-0 xl:grid-cols-none xl:grid-flow-col auto-cols-max justify-between">
> <div v-for="(item, index) in component.front_section_lang[0].data" :key="index"
<div> class="flex flex-col gap-[25px] sm:gap-8 max-w-[280px]">
<div <h3 class="h4-uppercase-bold-inter">{{ item.title }}</h3>
v-for="(item, key) in contact" <div class="cursor-pointer hover:text-text-light/80 dark:hover:text-text-dark/70 transition-all text-inter"
:key="key" v-for="(el, indexEl) in item.children" :key="indexEl">
class="flex flex-col gap-[25px] sm:gap-8 max-w-[280px]" {{ el.title }}
>
<h3 v-if="key == 'header'"
class="h4-uppercase-bold-inter"
>
{{ item }}
</h3>
<div v-else
class="transition-all text-inter"
>
{{ item }}
</div>
</div>
<div class="text-red-500">
arina - doadj tu icone i style
</div>
<a
href="/lei_certificate_aurrie.pdf"
target="_blank"
rel="noopener noreferrer"
>{{ $t("Footer.Lei") }}</a>
</div>
<div
v-for="(section, index) in docs"
:key="index"
class="flex flex-col gap-[25px] sm:gap-8 max-w-[280px]"
>
<h3 class="h4-uppercase-bold-inter">
{{ section.translation }}
</h3>
<div v-for="(item, key) in section.data"
:key="key"
>
<div
class="cursor-pointer hover:text-text-light/80 dark:hover:text-text-dark/70 transition-all text-inter"
>
<a target="_blank"
:href="`/api/public/document/${item.name}`"
>{{
item.translation
}}</a>
</div>
</div> </div>
</div> </div>
</div> </div>
<ClientOnly v-if="!colorMode?.forced"> <ClientOnly v-if="!colorMode?.forced">
<img <img class="cursor-pointer w-[70%] sm:w-[50%] xl:w-[30%]"
class="cursor-pointer w-[70%] sm:w-[50%] xl:w-[30%]" :src="isDark ? '/logo-footer-dark.svg' : '/logo-footer.svg'" alt="logo" @click="menuStore.navigateToItem()" />
:src="isDark ? '/logo-footer-dark.svg' : '/logo-footer.svg'"
alt="logo"
@click="menuStore.navigateToItem()"
>
</ClientOnly> </ClientOnly>
</UiContainer> </UiContainer>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { GenericResponse, Footer } from '~/types' defineProps<{
component: {
id: number
name: string
img: string[]
component_name: string
is_no_lang: boolean
page_name: string
front_section_lang: {
data: {
title: string
children: {
title: string
value: 'email' | 'address' | 'certificate' | 'document' | 'form' | 'info'
link?: string
}[]
}[]
id_front_section: number
id_lang: number
}[]
}
}>();
const menuStore = useMenuStore() const menuStore = useMenuStore();
const colorMode = useColorMode() const colorMode = useColorMode();
const { data } = await useMyFetch<GenericResponse<Footer>>( const isDark = computed({
'/api/public/front/footer', get() {
{}, return colorMode.value === "dark";
) },
set(_isDark) {
const contact = ref(data.data.contact) colorMode.preference = _isDark ? "dark" : "light";
const docs = ref(data.data.docs) },
});
const isDark = computed(() => colorMode.value === 'dark')
</script> </script>

View File

@ -1,35 +1,25 @@
<template> <template>
<UiContainer> <UiContainer>
<div class="space-25-55"> <div class="space-25-55">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ component.front_section_lang[0].data.title }}</h2>
{{ component.front_section_lang[0].data.title }}
</h2>
<div class="flex flex-col gap-10"> <div class="flex flex-col gap-10">
<div <div v-for="(item, index) in component.front_section_lang[0].data.faq" :key="index"
v-for="(item, index) in component.front_section_lang[0].data.faq"
:key="index"
class="flex gap-8 sm:gap-20 md:gap-40 xl:gap-60 cursor-pointer"
@click="active = active === index ? 0 : index" @click="active = active === index ? 0 : index"
> class="flex gap-8 sm:gap-20 md:gap-40 xl:gap-60 cursor-pointer">
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
<span v-if="index + 1 < 10">0</span>{{ index + 1 }} <span v-if="index + 1 < 10">0</span>{{ index + 1 }}
</h4> </h4>
<div <div :class="[
:class="[
'flex justify-between w-full transition-all duration-300 gap-2 sm:gap-10 md:gap-2', 'flex justify-between w-full transition-all duration-300 gap-2 sm:gap-10 md:gap-2',
active === index active === index && 'pb-10 border-b border-bg-dark dark:border-bg-light',
&& 'pb-10 border-b border-bg-dark dark:border-bg-light', ]">
]"
>
<div class="max-w-[1200px] flex flex-col gap-6"> <div class="max-w-[1200px] flex flex-col gap-6">
<h4 <h4 :class="[
:class="[
'h4-uppercase-bold-inter transition-colors duration-300', 'h4-uppercase-bold-inter transition-colors duration-300',
active === index active === index &&
&& 'text-accent-green-light dark:text-accent-green-dark', 'text-accent-green-light dark:text-accent-green-dark',
]" ]">
>
{{ item.label }} {{ item.label }}
</h4> </h4>
@ -40,36 +30,21 @@
</transition> </transition>
</div> </div>
<svg <svg class="min-w-5 h-5 dark:text-bg-light" viewBox="0 0 20 21" fill="none"
class="min-w-5 h-5 dark:text-bg-light" xmlns="http://www.w3.org/2000/svg">
viewBox="0 0 20 21" <path :d="active === index
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
:d="
active === index
? 'M1.29289 17.4628L0.585786 18.1699L2 19.5841L2.70711 18.877L1.29289 17.4628ZM19.9706 1.19936C19.9706 0.647074 19.5228 0.199359 18.9706 0.199359L9.97056 0.19936C9.41828 0.199359 8.97056 0.647075 8.97056 1.19936C8.97056 1.75164 9.41828 2.19936 9.97056 2.19936L17.9706 2.19936L17.9706 10.1994C17.9706 10.7516 18.4183 11.1994 18.9706 11.1994C19.5228 11.1994 19.9706 10.7516 19.9706 10.1994L19.9706 1.19936ZM2 18.1699L2.70711 18.877L19.6777 1.90647L18.9706 1.19936L18.2635 0.492253L1.29289 17.4628L2 18.1699Z' ? 'M1.29289 17.4628L0.585786 18.1699L2 19.5841L2.70711 18.877L1.29289 17.4628ZM19.9706 1.19936C19.9706 0.647074 19.5228 0.199359 18.9706 0.199359L9.97056 0.19936C9.41828 0.199359 8.97056 0.647075 8.97056 1.19936C8.97056 1.75164 9.41828 2.19936 9.97056 2.19936L17.9706 2.19936L17.9706 10.1994C17.9706 10.7516 18.4183 11.1994 18.9706 11.1994C19.5228 11.1994 19.9706 10.7516 19.9706 10.1994L19.9706 1.19936ZM2 18.1699L2.70711 18.877L19.6777 1.90647L18.9706 1.19936L18.2635 0.492253L1.29289 17.4628L2 18.1699Z'
: 'M2.7364 1.49211L2.0293 0.785005L0.615083 2.19922L1.32219 2.90633L2.7364 1.49211ZM18.9999 20.1698C19.5521 20.1698 19.9999 19.7221 19.9999 19.1698L19.9999 10.1698C19.9999 9.6175 19.5521 9.16978 18.9999 9.16978C18.4476 9.16978 17.9999 9.6175 17.9999 10.1698L17.9999 18.1698L9.99986 18.1698C9.44758 18.1698 8.99986 18.6175 8.99986 19.1698C8.99986 19.7221 9.44757 20.1698 9.99986 20.1698L18.9999 20.1698ZM2.0293 2.19922L1.32219 2.90633L18.2928 19.8769L18.9999 19.1698L19.707 18.4627L2.7364 1.49211L2.0293 2.19922Z' : 'M2.7364 1.49211L2.0293 0.785005L0.615083 2.19922L1.32219 2.90633L2.7364 1.49211ZM18.9999 20.1698C19.5521 20.1698 19.9999 19.7221 19.9999 19.1698L19.9999 10.1698C19.9999 9.6175 19.5521 9.16978 18.9999 9.16978C18.4476 9.16978 17.9999 9.6175 17.9999 10.1698L17.9999 18.1698L9.99986 18.1698C9.44758 18.1698 8.99986 18.6175 8.99986 19.1698C8.99986 19.7221 9.44757 20.1698 9.99986 20.1698L18.9999 20.1698ZM2.0293 2.19922L1.32219 2.90633L18.2928 19.8769L18.9999 19.1698L19.707 18.4627L2.7364 1.49211L2.0293 2.19922Z'
" " fill="currentColor" />
fill="currentColor"
/>
</svg> </svg>
</div> </div>
</div> </div>
</div> </div>
<h4 <h4 class="h4-uppercase-bold-inter sm:text-start md:text-center xl:text-start xl:ml-66">
class="h4-uppercase-bold-inter sm:text-start md:text-center xl:text-start xl:ml-66" <span v-for="(item, index) in component.front_section_lang [0].data.sub_title" :key="index" :class="{
>
<span
v-for="(item, index) in component.front_section_lang[0].data
.sub_title"
:key="index"
:class="{
'text-accent-green-light dark:text-accent-green-dark': 'text-accent-green-light dark:text-accent-green-dark':
item.highlight, item.highlight,
}" }">
>
{{ item.text }} {{ item.text }}
</span> </span>
</h4> </h4>
@ -88,25 +63,26 @@ defineProps<{
page_name: string page_name: string
front_section_lang: { front_section_lang: {
data: { data: {
title: string title: string;
faq: [ faq: [
{ {
label: string label: string;
content: string content: string;
}, }
] ];
sub_title: [ sub_title: [
{ {
text: string text: string;
highlight: boolean highlight: boolean;
}, }
] ];
} }
id_front_section: number id_front_section: number
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const active = ref<number>(0)
const active = ref<number>(0);
</script> </script>

View File

@ -1,64 +1,42 @@
<template> <template>
<UiContainer class="space-y-[30px] xl:space-y-[55px]"> <UiContainer class="space-y-[30px] xl:space-y-[55px]">
<div class="flex flex-col xl:flex-row items-stretch w-full h-full gap-5"> <div class="flex flex-col xl:flex-row items-stretch w-full h-full gap-5">
<div <div class="flex flex-col justify-between items-center space-25-55 xl:gap-4 w-auto h-auto">
class="flex flex-col justify-between items-center space-25-55 xl:gap-4 w-auto h-auto"
>
<h1 class="h1"> <h1 class="h1">
<span <span v-for="(item, index) in component.front_section_lang[0].data.title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data.title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="index !== component.front_section_lang[0].data.title.length - 1">
v-if=" </span>
index !== component.front_section_lang[0].data.title.length - 1
"
/>
</span> </span>
</h1> </h1>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
<span <span v-for="(item, index) in component.front_section_lang[0].data.sub_title" :key="index" :class="{
v-for="(item, index) in component.front_section_lang[0].data
.sub_title"
:key="index"
:class="{
'text-accent-green-light dark:text-accent-green-dark': 'text-accent-green-light dark:text-accent-green-dark':
item.highlight, item.highlight,
}" }">
>
{{ item.text }} {{ item.text }}
</span> </span>
</h4> </h4>
<p>{{ component.front_section_lang[0].data.description }}</p> <p>{{ component.front_section_lang[0].data.description }}</p>
</div> </div>
<div <div class="w-full xl:max-w-[570px] h-[390px] sm:h-[506px] block rounded-2xl" :style="{
class="w-full xl:max-w-[570px] h-[390px] sm:h-[506px] block rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
<div class="flex flex-col xl:flex-row items-stretch w-full h-full gap-5"> <div class="flex flex-col xl:flex-row items-stretch w-full h-full gap-5">
<div <div class="w-full xl:max-w-[570px] h-[225px] block rounded-2xl" :style="{
class="w-full xl:max-w-[570px] h-[225px] block rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <div class="flex flex-col justify-between items-center space-25-55 xl:gap-4 w-auto h-auto">
<div
class="flex flex-col justify-between items-center space-25-55 xl:gap-4 w-auto h-auto"
>
<p>{{ component.front_section_lang[0].data.description_second }}</p> <p>{{ component.front_section_lang[0].data.description_second }}</p>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.sub_title_second }} {{ component.front_section_lang[0].data.sub_title_second }}
@ -95,5 +73,5 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -5,42 +5,35 @@
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.main_title }} {{ component.front_section_lang[0].data.main_title }}
</h4> </h4>
<div <div class="w-full h-full flex flex-col items-stretch gap-4 xl:flex-row space-25-55 xl:!space-y-0">
class="w-full h-full flex flex-col items-stretch gap-4 xl:flex-row space-25-55 xl:!space-y-0" <div class="flex flex-col space-y-[55px] xl:space-y-0 xl:justify-between">
>
<div
class="flex flex-col space-y-[55px] xl:space-y-0 xl:justify-between"
>
<p>{{ component.front_section_lang[0].data.main_description }}</p> <p>{{ component.front_section_lang[0].data.main_description }}</p>
<p>{{ component.front_section_lang[0].data.section_title }}</p> <p>{{ component.front_section_lang[0].data.section_title }}</p>
<ul> <ul>
<li <li class="" v-for="(item, index) in component.front_section_lang[0].data
v-for="(item, index) in component.front_section_lang[0].data .section_items" :key="index">
.section_items"
:key="index"
class=""
>
<span>{{ index + 1 }}. </span>{{ item }} <span>{{ index + 1 }}. </span>{{ item }}
</li> </li>
</ul> </ul>
</div> </div>
<div <div class="w-full xl:max-w-[690px] h-[200px] sm:h-[390px] block rounded-2xl" :style="{
class="w-full xl:max-w-[690px] h-[200px] sm:h-[390px] block rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
</div> </div>
<div class="flex items-center justify-center w-[100%] xl:w-[90%] mx-auto"> <div class="flex items-center justify-center w-[60%] mx-auto">
<div class="flex flex-col items-center p-3 sm:p-6 md:p-8 xl:p-11 border border-block rounded-2xl gap-5"> <div class="flex flex-col items-center p-3 sm:p-6 md:p-8 xl:p-11 border border-block rounded-2xl gap-5">
<h4 class="h4-uppercase-bold-inter text-accent-green-light dark:text-accent-green-dark"> <h4 class="h4-uppercase-bold-inter text-accent-green-light dark:text-accent-green-dark">
20 {{ $t("years") }} 20 {{ $t("years") }}
</h4> </h4>
<img class="dark:hidden" :src="`/api/public/chart?second_color=000&base_color=c7c7c7&width=1800&height=480`" alt=""> <img class="dark:hidden"
<img class="hidden dark:block" :src="`/api/public/chart?second_color=fff&base_color=9a7f62&width=1800&height=480`" alt=""> :src="`/api/public/file/${component.img[2]}_l.webp`"
alt="" />
<img class="hidden dark:block"
:src="`/api/public/file/${component.img[3]}_l.webp`"
alt="" />
</div> </div>
</div> </div>
</div> </div>
@ -50,19 +43,13 @@
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
{{ component.front_section_lang[0].data.info_title }} {{ component.front_section_lang[0].data.info_title }}
</h4> </h4>
<div <div v-html="component.front_section_lang[0].data.info_description_second" class=""></div>
class=""
v-html="component.front_section_lang[0].data.info_description_second"
/>
</div> </div>
<div <div class="w-full xl:max-w-[690px] h-[170px] sm:h-[360px] block rounded-2xl" :style="{
class="w-full xl:max-w-[690px] h-[170px] sm:h-[360px] block rounded-2xl"
:style="{
backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[1]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
@ -90,5 +77,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -2,22 +2,15 @@
<UiContainer> <UiContainer>
<div class="space-25-55-75 max-w-[1000px] mx-auto"> <div class="space-25-55-75 max-w-[1000px] mx-auto">
<h1 class="h2-bold-bounded"> <h1 class="h2-bold-bounded">
<span <span v-for="(item, index) in component.front_section_lang[0].data.title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data.title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="index !== component.front_section_lang[0].data.title.length - 1">
v-if=" </span>
index !== component.front_section_lang[0].data.title.length - 1
"
/>
</span> </span>
</h1> </h1>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">
@ -28,7 +21,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ defineProps<{
component: { component: {
id: number id: number
name: string name: string
@ -48,5 +41,6 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
</script> </script>

View File

@ -1,22 +1,15 @@
<template> <template>
<UiContainer class="space-25-75"> <UiContainer class="space-25-75">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">
<span <span v-for="(item, index) in component.front_section_lang[0].data.main_title" :key="index" :class="[
v-for="(item, index) in component.front_section_lang[0].data.main_title"
:key="index"
:class="[
item.highlight item.highlight
? 'text-accent-green-light dark:text-accent-green-dark' ? 'text-accent-green-light dark:text-accent-green-dark'
: '', : '',
'inline', 'inline',
]" ]">
>
{{ item.text }} {{ item.text }}
<span <span v-if="index !== component.front_section_lang[0].data.main_title.length - 1">
v-if=" </span>
index !== component.front_section_lang[0].data.main_title.length - 1
"
/>
</span> </span>
</h2> </h2>
@ -29,26 +22,41 @@
<!-- products --> <!-- products -->
<div class="space-25-55-75 flex flex-col items-center"> <div class="space-25-55-75 flex flex-col items-center">
<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',
]" ]">
> <div v-for="(item, index) in productStore.productList" :key="index"
<Product v-for="product in productStore.productList" :key="product.id" :product="product" /> 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">
<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 gap-[10px] sm:gap-[15px] w-full">
<h3 class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark">
{{ item.name }}
</h3>
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
{{ item.tax_name }}
</p>
</div>
<div class="flex items-center justify-between">
<p class="text-accent-green-light text-bold-24">
{{ item.formatted_price }}
</p>
<button @click="productStore.incrementCartItem(item.id)"
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>
</button>
</div>
</div>
</div>
</div> </div>
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getShopMenu()?.id, slug: menuStore.getShopMenu()?.front_menu_lang.at(0)?.link_rewrite } }"> <UiButtonArrow @click="menuStore.navigateToShop" :arrow="true" class="mx-auto" type="fill">{{ $t('eshop') }}</UiButtonArrow>
<UiButtonArrow type="fill" :arrow="true">
{{ $t("eshop") }}
</UiButtonArrow>
</nuxt-link>
</div> </div>
<!-- calculator-block --> <!-- calculator-block -->
<div <div class="flex flex-col xl:flex-row items-stretch gap-6 sm:gap-2 pt-5 sm:p-0 space-25-55">
class="flex flex-col xl:flex-row items-stretch gap-6 sm:gap-2 pt-5 sm:p-0 space-25-55"
>
<div class="flex flex-col space-y-[55px] sm:justify-between"> <div class="flex flex-col space-y-[55px] sm:justify-between">
<div class="space-25-55"> <div class="space-25-55">
<p>{{ component.front_section_lang[0].data.section_description }}</p> <p>{{ component.front_section_lang[0].data.section_description }}</p>
@ -63,9 +71,7 @@
</div> </div>
<!-- calculator --> <!-- calculator -->
<div <div class="w-full md:min-w-[680px] p-[25px] md:p-[50px] border border-button rounded-2xl block">
class="w-full md:min-w-[680px] p-[25px] md:p-[50px] border border-button rounded-2xl block"
>
<h2 class="h2-bold-bounded text-center mb-10 sm:mb-20"> <h2 class="h2-bold-bounded text-center mb-10 sm:mb-20">
{{ component.front_section_lang[0].data.calculator_title }} {{ component.front_section_lang[0].data.calculator_title }}
</h2> </h2>
@ -73,61 +79,35 @@
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex justify-between"> <div class="flex justify-between">
<p>{{ $t("monthly_savings") }}</p> <p>{{ $t("monthly_savings") }}</p>
<p <p class="text-accent-green-light dark:text-accent-green-dark font-bold">
class="text-accent-green-light dark:text-accent-green-dark font-bold" {{ store.monthlySavings }} {{ menuStore.selectedCurrency?.sign }}
>
{{ store.monthlySavings }}
{{ menuStore.selectedCurrency?.sign }}
</p> </p>
</div> </div>
<input <input v-model="store.monthlySavings" type="range" max="600" :min="store.minValue"
v-model="store.monthlySavings" class="w-full accent-button cursor-pointer" @mouseup="store.getCalculator()"
type="range" @touchend="store.getCalculator()" />
max="600"
:min="store.minValue"
class="w-full accent-button cursor-pointer"
@mouseup="store.getCalculator()"
@touchend="store.getCalculator()"
>
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div class="flex justify-between"> <div class="flex justify-between">
<p>{{ $t("storage_period_years") }}</p> <p>{{ $t("storage_period_years") }}</p>
<p <p class="text-accent-green-light dark:text-accent-green-dark font-bold">
class="text-accent-green-light dark:text-accent-green-dark font-bold"
>
{{ store.storagePeriod }} {{ store.storagePeriod }}
</p> </p>
</div> </div>
<input <input v-model="store.storagePeriod" type="range" max="20" class="w-full accent-button cursor-pointer"
v-model="store.storagePeriod" @mouseup="store.getCalculator()" @touchend="store.getCalculator()" />
type="range"
max="20"
class="w-full accent-button cursor-pointer"
@mouseup="store.getCalculator()"
@touchend="store.getCalculator()"
>
</div> </div>
</div> </div>
<div <div class="flex flex-col items-start sm:flex-row gap-6 sm:gap-1 justify-between sm:items-center">
class="flex flex-col items-start sm:flex-row gap-6 sm:gap-1 justify-between sm:items-center"
>
<div class=""> <div class="">
<p>{{ $t("expected_savings_value") }}</p> <p>{{ $t("expected_savings_value") }}</p>
<h2 <h2 class="h2-bold-bounded text-accent-green-light dark:text-accent-green-dark">
class="h2-bold-bounded text-accent-green-light dark:text-accent-green-dark"
>
{{ menuStore.selectedCurrency?.sign }} {{ store.totalInvestment }} {{ menuStore.selectedCurrency?.sign }} {{ store.totalInvestment }}
</h2> </h2>
</div> </div>
<UiButtonArrow :arrow="true" <UiButtonArrow :arrow="true" type="fill" class="mx-auto sm:m-0">{{
type="fill"
class="mx-auto sm:m-0"
>
{{
component.front_section_lang[0].data.button component.front_section_lang[0].data.button
}} }}</UiButtonArrow>
</UiButtonArrow>
</div> </div>
</div> </div>
</div> </div>
@ -135,8 +115,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Product from './Product.vue'
defineProps<{ defineProps<{
component: { component: {
id: number id: number
@ -164,33 +142,34 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const store = useStore()
const menuStore = useMenuStore()
const itemCount = ref(4) const store = useStore();
const productStore = useProductStore() const menuStore = useMenuStore();
const itemCount = ref(4);
const productStore = useProductStore();
async function updateItemCount() { async function updateItemCount() {
const width = window.innerWidth const width = window.innerWidth;
if (width >= 1800) itemCount.value = 5 if (width >= 1800) itemCount.value = 5;
else if (width >= 1600) itemCount.value = 4 else if (width >= 1200) itemCount.value = 4;
else if (width >= 1200) itemCount.value = 3 else if (width >= 768) itemCount.value = 3;
else if (width >= 640) itemCount.value = 2 else if (width >= 640) itemCount.value = 2;
else itemCount.value = 1 else itemCount.value = 1;
} }
watch(itemCount, async () => { watch(itemCount, async () => {
await productStore.getList(itemCount.value) await productStore.getList(itemCount.value);
}) });
onMounted(async () => { onMounted(async () => {
await updateItemCount() await updateItemCount();
window.addEventListener('resize', updateItemCount) window.addEventListener("resize", updateItemCount);
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', updateItemCount) window.removeEventListener("resize", updateItemCount);
}) });
</script> </script>

View File

@ -5,52 +5,38 @@
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" /> }" />
<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 flex-wrap-reverse gap-y-4 justify-between"> <div class="flex flex-wrap-reverse gap-y-4 justify-between">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ $t('login') }}</h2>
{{ $t("login") }} <button @click="menuStore.navigateToItem()"
</h2> 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">{{
<button $t('back_to_home') }}</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"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
</div> </div>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">{{ $t('email') }}</p>
{{ $t("email") }} <input v-model="userStore.email" :placeholder="$t('email')" type="text"
</p> 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" />
<input data-testid="login-email" v-model="userStore.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>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">{{ $t('password') }}</p>
{{ $t("password") }} <input v-model="userStore.password" :placeholder="$t('placeholder_password')" type="text"
</p> 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" />
<input data-testid="login-password" v-model="userStore.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>
</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 data-testid="login-btn" type="fill" :arrow="true" @click="userStore.logIn()"> <UiButtonArrow @click="userStore.logIn()" type="fill" :arrow="true">{{ $t('login') }}</UiButtonArrow>
{{
$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> <p class="cursor-pointer hover:underline transition-all">{{
{{ $t("no_account") }} $t('no_account')
</p> }}</p>
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getRegistrationMenu()?.id, slug: menuStore.getRegistrationMenu()?.front_menu_lang.at(0)?.link_rewrite } }"> <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>
</nuxt-link>
</div> </div>
</div> </div>
</UiContainer> </UiContainer>
@ -61,36 +47,27 @@
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" /> }" />
<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 flex-wrap-reverse gap-y-4 justify-between"> <div class="flex flex-wrap-reverse gap-y-4 justify-between">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ $t('verify_login') }}</h2>
{{ $t("verify_login") }} <button @click="menuStore.navigateToItem()"
</h2> 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">{{
<button $t('back_to_home') }}</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"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
</div> </div>
<div class="space-y-[30px]"> <div class="space-y-[30px]">
<p> <p>{{ $t('send_code') }} <span class="text-button font-semibold">{{ userStore.email }}</span> {{
{{ $t("send_code") }} $t('by_email') }}
<span class="text-button font-semibold">{{ userStore.email }}</span>
{{ $t("by_email") }}
</p> </p>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">{{ $t('code') }}</p>
{{ $t("code") }} <input v-model="userStore.vCode" :placeholder="$t('code')" type="text"
</p> 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" />
<input data-testid="code-input" v-model="userStore.vCode" :placeholder="$t('code')" 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>
</div> </div>
</div> </div>
<div class="py-[25px] sm:py-12 flex justify-center w-full"> <div class="py-[25px] sm:py-12 flex justify-center w-full">
<UiButtonArrow data-testid="code-confirm" type="fill" :arrow="true" @click="userStore.sendFormCode(true)"> <UiButtonArrow @click="userStore.sendFormCode(true)" type="fill" :arrow="true">{{ $t('confirm') }}
{{ $t("confirm") }}
</UiButtonArrow> </UiButtonArrow>
</div> </div>
</div> </div>
@ -107,11 +84,13 @@ defineProps<{
is_no_lang: boolean is_no_lang: boolean
page_name: string page_name: string
front_section_lang: { front_section_lang: {
data: {
}
id_front_section: number id_front_section: number
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const userStore = useUserStore() const userStore = useUserStore()
const menuStore = useMenuStore() const menuStore = useMenuStore()
</script> </script>

View File

@ -1,39 +1,28 @@
<template> <template>
<UiContainer class="flex flex-wrap items-center justify-between gap-[30px] sm:gap-y-[55px] xl:gap-y-[100px]"> <UiContainer class="flex flex-wrap items-center justify-between gap-[30px] sm:gap-y-[55px] xl:gap-y-[100px]">
<div v-for="(item, index) in component.front_section_lang[0].data" :key="index" <div class="w-full flex flex-col gap-[25px] xl:max-w-[48%] md:mx-10 xl:m-0"
class="w-full flex flex-col gap-[25px] xl:max-w-[48%] md:mx-10 xl:m-0"> v-for="(item, index) in component.front_section_lang[0].data" :key="index">
<!-- xl --> <!-- xl -->
<div class="hidden xl:flex xl:h-[330px] flex-col justify-between"> <div class="hidden xl:flex xl:h-[330px] flex-col justify-between">
<div class="space-y-[55px]"> <div class="space-y-[55px]">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ item.title }}</h2>
{{ item.title }}
</h2>
<p>{{ item.description }}</p> <p>{{ item.description }}</p>
</div> </div>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">{{ item.sub_title }}</h4>
{{ item.sub_title }}
</h4>
</div> </div>
<!-- sm/md --> <!-- sm/md -->
<div class="xl:hidden flex flex-col gap-y-[25px] sm:gap-y-[55px]"> <div class="xl:hidden flex flex-col gap-y-[25px] sm:gap-y-[55px]">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ item.title }}</h2>
{{ item.title }}
</h2>
<p>{{ item.description }}</p> <p>{{ item.description }}</p>
<h4 class="h4-uppercase-bold-inter"> <h4 class="h4-uppercase-bold-inter">{{ item.sub_title }}</h4>
{{ item.sub_title }}
</h4>
</div> </div>
<UiImgWrapper :src="`/api/public/file/${component.img[index]}_l.webp`"> <UiImgWrapper :src="`/api/public/file/${component.img[index]}_l.webp`">
<template #button> <template #button>
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: linksInfo[index].id, slug: linksInfo[index].slug } }"> <UiButtonArrow :arrow="true">{{ item.title }}</UiButtonArrow>
<UiButtonArrow :arrow="true">
{{ item.title }}
</UiButtonArrow>
</nuxt-link>
</template> </template>
</UiImgWrapper> </UiImgWrapper>
</div> </div>
<!-- Map block with same layout rules --> <!-- Map block with same layout rules -->
@ -44,11 +33,11 @@
<MapBlock /> <MapBlock />
<div class="flex items-center gap-4 w-full justify-end"> <div class="flex items-center gap-4 w-full justify-end">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-3 h-3 rounded-[2.8px] bg-button" /> <div class="w-3 h-3 rounded-[2.8px] bg-button"></div>
<p>Partners</p> <p>Partners</p>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-3 h-3 rounded-[2.8px] bg-block" /> <div class="w-3 h-3 rounded-[2.8px] bg-block"></div>
<p>Customers</p> <p>Customers</p>
</div> </div>
</div> </div>
@ -76,31 +65,6 @@ type Component = {
} }
defineProps<{ defineProps<{
component: Component component: Component;
}>() }>();
const menuStore = useMenuStore()
const linksInfo = ref([
{
id: menuStore.getInvestitionMenu()?.id,
slug: menuStore.getInvestitionMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
{
id: menuStore.getAboutGoldMenu()?.id,
slug: menuStore.getAboutGoldMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
{
id: menuStore.getBusinessMenu()?.id,
slug: menuStore.getBusinessMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
{
id: menuStore.getAboutUsMenu()?.id,
slug: menuStore.getAboutUsMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
{
id: menuStore.getContactMenu()?.id,
slug: menuStore.getContactMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
])
</script> </script>

View File

@ -10,24 +10,15 @@
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <div class="flex flex-col items-center space-y-[30px] sm:flex-row sm:items-start md:space-y-0 xl:items-center">
<div
class="flex flex-col items-center space-y-[30px] sm:flex-row sm:items-start md:space-y-0 xl:items-center"
>
<h3 class="h4-uppercase-bold-inter sm:min-w-[45%]"> <h3 class="h4-uppercase-bold-inter sm:min-w-[45%]">
{{ component.front_section_lang[0].data.title_second }} {{ component.front_section_lang[0].data.title_second }}
</h3> </h3>
<div class="flex w-full items-start justify-center sm:justify-end"> <div class="flex w-full items-start justify-center sm:justify-end">
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getRegistrationMenu()?.id, slug: menuStore.getRegistrationMenu()?.front_menu_lang.at(0)?.link_rewrite } }"> <UiButtonArrow :arrow="true" type="fill">{{
<UiButtonArrow :arrow="true"
type="fill"
>
{{
component.front_section_lang[0].data.button component.front_section_lang[0].data.button
}} }}</UiButtonArrow>
</UiButtonArrow>
</nuxt-link>
</div> </div>
</div> </div>
</div> </div>
@ -53,7 +44,7 @@ defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const menuStore = useMenuStore()
</script> </script>

View File

@ -1,33 +1,47 @@
<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]">
<!-- product -->
<div class="space-25-55-75 flex flex-col items-center">
<div :class="[ <div :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 v-for="product in productStore.productList" :key="product.id" :product="product" /> <!-- product -->
<div v-for="(item, index) in productStore.productList" :key="index"
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">
<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 gap-[10px] sm:gap-[15px] w-full">
<h3 class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark">
{{ item.name }}
</h3>
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
{{ item.tax_name }}
</p>
</div>
<div class="flex items-center justify-between">
<p class="text-accent-green-light text-bold-24">
{{ item.formatted_price }}
</p>
<button @click="productStore.incrementCartItem(item.id)"
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>
</button>
</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 class="h4-uppercase-bold-inter w-full text-center md:text-start xl:max-w-[50%]"> <h3 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>
<nuxt-link <UiButtonArrow @click="menuStore.navigateToShop" type="fill" :arrow="true">{{ $t('eshop') }}</UiButtonArrow>
:to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getShopMenu()?.id, slug: menuStore.getShopMenu()?.front_menu_lang.at(0)?.link_rewrite } }">
<UiButtonArrow type="fill" :arrow="true">
{{ $t("eshop") }}
</UiButtonArrow>
</nuxt-link>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount } from 'vue' import { ref, onMounted, onBeforeUnmount } from "vue";
import Product from './Product.vue'
defineProps<{ defineProps<{
component: { component: {
@ -38,31 +52,32 @@ defineProps<{
is_no_lang: boolean is_no_lang: boolean
page_name: string page_name: string
} }
}>() }>();
const menuStore = useMenuStore() const menuStore = useMenuStore()
const itemCount = ref(4) const itemCount = ref(4);
const productStore = useProductStore() const productStore = useProductStore();
async function updateItemCount() { async function updateItemCount() {
const width = window.innerWidth const width = window.innerWidth;
if (width >= 1800) itemCount.value = 5 if (width >= 1800) itemCount.value = 5;
else if (width >= 1600) itemCount.value = 4 else if (width >= 1200) itemCount.value = 4;
else if (width >= 1200) itemCount.value = 3 else if (width >= 768) itemCount.value = 3;
else if (width >= 640) itemCount.value = 2 else if (width >= 640) itemCount.value = 2;
else itemCount.value = 1 else itemCount.value = 1;
} }
watch(itemCount, async () => { watch(itemCount, async () => {
await productStore.getList(itemCount.value) await productStore.getList(itemCount.value);
}) });
onMounted(async () => { onMounted(async () => {
await updateItemCount() await updateItemCount();
window.addEventListener('resize', updateItemCount) window.addEventListener("resize", updateItemCount);
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('resize', updateItemCount) window.removeEventListener("resize", updateItemCount);
}) });
</script> </script>

View File

@ -1,65 +1,36 @@
<template> <template>
<div>
<nuxt-link :to="{
name: `id-slug___${$i18n.locale}`,
params: {
id: menuStore.getProductMenu()?.id,
slug: menuStore.getProductMenu()?.front_menu_lang.at(0)?.link_rewrite,
},
query: {
prod_id: product.id,
name: product.link_rewrite,
},
}">
<div <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] h-full"> 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="`/api/public/file/${product.cover_picture_uuid}.webp`" alt="Product Image" <img :src="`https://www.yourgold.cz/api/public/file/${props.product?.cover_picture_uuid}.webp`" alt="Product Image"
class="h-[95px] sm:h-[180px] md:h-[205px] rounded-[5px]" @error="handleImageError" /> class="h-[95px] sm:h-[180px] md:h-[205px] rounded-[5px]"
onerror="this.onerror=null; this.src='/photo.svg';" />
<div class="flex flex-col justify-between h-full w-full gap-[7px] sm:gap-[15px]"> <div class="flex flex-col justify-between h-full w-full gap-[7px] sSm:gap-[15px]">
<div class="flex flex-col gap-[7px] sm:gap-[15px] w-full"> <div class="flex flex-col gap-[7px] sm:gap-[15px] w-full">
<h3 <h3 class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] text-bg-dark">
class="text-[10px] sm:text-base md:text-lg text-xl font-bold leading-[130%] sm:leading-[150%] text-bg-dark"> {{ props.product?.name }}
{{ product.name }}
</h3> </h3>
<p class="text-[9px] sm:text-[12px] text-sm text-bg-dark"> <p class="text-[9px] sm:text-[12px] text-sm text-bg-dark">
{{ product.tax_name }} {{ props.product?.tax_name }}
</p> </p>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<p <p class="text-accent-green-light font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold">
class="text-accent-green-light font-inter text-[12px] sm:text-[21px] md:text-2xl leading-[150%] font-bold"> {{ props.product?.formatted_price }}
{{ product.formatted_price }}
</p> </p>
<button <button @click="productStore.incrementCartItem(props.product?.id)"
class="w-[22px] h-[22px] sm:w-9 sm:h-9 md:w-12 md:h-12 rounded-[5px] text-bg-light sm:rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center p-1" class="w-[22px] h-[22px] sm:w-9 sm:h-9 md:w-12 md:h-12 rounded-[5px] text-bg-light sm:rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center p-1">
@click.stop.prevent="productStore.incrementCartItem(product.id)"> <i class="uil uil-shopping-cart text-lg sm:text-2xl md:text-[31px] cursor-pointer"></i>
<i class="uil uil-shopping-cart text-lg sm:text-2xl md:text-[31px]" />
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</nuxt-link>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
interface Product { const props = defineProps({
id: number product: Object,
name: string });
link_rewrite: string
tax_name: string
formatted_price: string
cover_picture_uuid: string
}
defineProps<{ product: Product }>()
const productStore = useProductStore() const productStore = useProductStore()
const menuStore = useMenuStore()
function handleImageError(event: Event) {
const img = event.target as HTMLImageElement
img.src = '/photo.svg'
}
</script> </script>

View File

@ -1,300 +0,0 @@
<template>
<UiContainer>
<transition>
<div v-if="showPopup && curImage" ref="dropdownRef" tabindex="0"
class="fixed top-0 left-0 bg-[#000000cc] w-screen h-screen z-50 flex items-center justify-center cursor-pointer p-10"
@keyup.esc="showPopup = false" @keyup.left="goLeft" @keyup.right="goRight" @click.self="showPopup = false">
<div class="border border-white p-4 rounded-2xl shadow-2xl bg-white cursor-auto">
<img :src="`/api/public/file/${curImage}_l.webp`" class="max-h-[80vh] object-contain" alt=""
@load="focusOnImgLoad">
</div>
<button class="absolute top-8 p-8 right-8 text-4xl rotate-45 text-white" @click="showPopup = false">
+
</button>
</div>
</transition>
<div class="space-y-10 sm:space-y-[75px]">
<div class="flex flex-col items-start xl:flex-row gap-[40px] sm:gap-[100px] border-b-2 border-block pb-[25px]">
<!-- images block -->
<button
class="xl:hidden 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"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
<div class="w-full flex-col-reverse sm:flex-row sm:w-[70%] mx-auto justify-center xl:w-auto flex gap-[25px]">
<div class="flex justify-center sm:justify-start sm:flex-col gap-[15px]">
<div v-for="image in product.picture_uuids" :key="image"
:class="['cursor-pointer hover:border-button-disabled transition-all h-[100px] w-[90px] border py-3 flex items-center justify-center rounded-[8px]', image === curImage ? 'border-block' : 'border-button']"
@click="curImage = image">
<img :src="`/api/public/file/${image}_m.webp`" :alt="product.name" srcset="" class="h-full w-auto">
</div>
</div>
<div class="cursor-pointer h-full flex justify-center sm:items-start" @click="showPopup = true">
<img :src="`/api/public/file/${curImage}_m.webp`"
class="sm:min-w-[400px] sm:max-w-[410px] xl:min-w-[300px] xl:max-w-[320px] w-full h-auto" alt="">
</div>
</div>
<!-- info block -->
<div class="w-full space-y-[50px]">
<div class="w-full space-y-[30px]">
<div class="space-y-[15px]">
<div class="flex items-center justify-between w-full">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ product.name }}
</h1>
<button
class="hidden xl:block 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"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
</div>
<p class="text-button font-inter text-base leading-[150%] uppercase sm:text-[20px]">
Title: {{
product.reference }}
</p>
</div>
<div class="flex flex-col gap-5" v-html="product.description" />
<div
class="flex flex-col sm:flex-row sm:items-end gap-[25px] sm:justify-between border-t-2 border-block pt-[15px]">
<div class="space-y-4">
<div class="space-y-2">
<h4 class="text-accent-green-light font-inter text-2xl leading-[150%] font-bold">
{{ formattedPrice }}
</h4>
<p>{{ product.tax_name }}</p>
</div>
<p class="font-medium">
{{ component.front_section_lang[0].data.purchase_price }}<span
class="text-button">{{ formattedBuyOutPrice
}}</span>
</p>
</div>
<div class="mx-auto sm:mx-0 group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap">
<button
class="h-[40px] cursor-pointer min-w-40 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">
Add to cart
</button>
<div :disabled="!product.sale_active" class="
flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px] bg-button text-text-dark group-hover:bg-button-hover
" @click="productStore.incrementCartItem(product.id)">
<i class="uil uil-shopping-cart text-[23px] sm:text-[33px]"></i>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-5">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ component.front_section_lang[0].data.product_details }}
</h1>
<div class="flex flex-col gap-5 mx-[50px]">
<div v-for="feature in product.features" :key="feature.feature" class="flex gap-4 justify-between">
<span class="mx-4">{{ feature.feature }}:</span>
<span class="mx-4">
{{ feature.value }}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="space-y-[55px]">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ component.front_section_lang[0].data.recommendations }}
</h1>
<!-- products -->
<div class="flex flex-wrap justify-center gap-5 sm:gap-10">
<div v-for="(item, index) in product.recommendations" :key="index">
<nuxt-link :to="{
name: `id-slug___${$i18n.locale}`,
params: {
id: menuStore.getProductMenu()?.id,
slug: menuStore.getProductMenu()?.front_menu_lang[0]
.link_rewrite,
},
query: { prod_id: item?.id, name: item?.link_rewrite },
}">
<div
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 h-full">
<img :src="`/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 gap-[10px] sm:gap-[15px] w-full">
<h3 class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark">
{{ item.name }}
</h3>
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
{{ item.tax_name }}
</p>
</div>
<div class="flex items-center justify-between">
<p class="text-accent-green-light text-bold-24">
{{ item.formatted_price }}
</p>
<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"
@click="productStore.incrementCartItem(item.id)">
<i class="uil uil-shopping-cart text-[25px] md:text-[24px] text-bg-light" />
</button>
</div>
</div>
</div>
</nuxt-link>
</div>
</div>
</div>
</div>
</UiContainer>
</template>
<script setup lang="ts">
import type { GenericResponse, ProductItem } from '~/types'
defineProps<{
component: {
id: number
name: string
img: string[]
component_name: string
is_no_lang: boolean
page_name: string
front_section_lang: {
data: {
purchase_price: string
product_details: string
recommendations: string
}
id_front_section: number
id_lang: number
}[]
}
}>()
const menuStore = useMenuStore()
const { $session } = useNuxtApp()
const productStore = useProductStore()
const dropdownRef = ref()
// const addToCart = (p: ProductItem) => {
// alert('add to cart product: ' + p.name)
// }
const formatPrice = (p: number) => {
const formatdecimal = new Intl.NumberFormat(
$session.cookieData.value.country.iso_code,
{
style: 'decimal',
maximumFractionDigits: $session.cookieData.value.currency.precision,
},
).format(p)
return $session.cookieData.value.currency.suffix
? $session.cookieData.value.currency.sign + ' ' + formatdecimal
: formatdecimal + ' ' + $session.cookieData.value.currency.sign
}
const focusOnImgLoad = () => {
dropdownRef.value.focus()
}
const formattedPrice = computed(() => {
return formatPrice(product.value.price)
})
const formattedBuyOutPrice = computed(() => {
return formatPrice(product.value.buyout_price)
})
const route = useRoute()
// if (!route.query?.prod_id) {
// throw createError({
// statusCode: 404,
// statusMessage: 'Not Found',
// fatal: true
// });
// }
const getProduct = async (): Promise<ProductItem> => {
if (route.query?.prod_id) {
const data = await useMyFetch<GenericResponse<ProductItem>>(
`/api/public/product/${route.query?.prod_id}`,
{},
)
// await new Promise(resolve => setTimeout(resolve, 200));
return data.data
}
return {} as ProductItem
}
const product = ref(await getProduct())
watch(
() => route.query?.prod_id,
async () => {
const data = await getProduct()
product.value = data
if (curImage.value != data.picture_uuids[0]) {
curImage.value = data.cover_picture_uuid
}
},
)
watch($session.cookieData, async () => {
const oldId = product.value.id
const data = await getProduct()
product.value = data
if (oldId != data.id) {
curImage.value = data.cover_picture_uuid
}
})
const showPopup = ref(false)
const curImage = ref(product.value.cover_picture_uuid)
watch(showPopup, (newValue) => {
if (newValue) {
document.body.style.overflow = 'hidden'
}
else {
document.body.style.overflow = 'unset'
}
})
const goLeft = () => {
if (!curImage.value) return
const index = product.value.picture_uuids.indexOf(curImage.value)
if (index > 0) {
curImage.value = product.value.picture_uuids[index - 1]
}
else {
curImage.value
= product.value.picture_uuids[product.value.picture_uuids.length - 1]
}
}
const goRight = () => {
if (!curImage.value) return
const index = product.value.picture_uuids.indexOf(curImage.value)
if (index < product.value.picture_uuids.length - 1) {
curImage.value = product.value.picture_uuids[index + 1]
}
else {
curImage.value = product.value.picture_uuids[0]
}
}
</script>
<style>
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>

View File

@ -1,170 +1,99 @@
<template> <template>
<UiContainer class="flex py-[15px] xl:py-20 sm:py-0"> <UiContainer class="flex py-[15px] xl:py-20 sm:py-0">
<div <div class="hidden xl:block rounded-2xl min-w-[40%] h-[830px]" :style="{
class="hidden xl:block rounded-2xl min-w-[40%] h-[830px]"
:style="{
backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`, backgroundImage: `url('/api/public/file/${component.img[0]}_l.webp')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <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 flex-wrap-reverse gap-y-4 justify-between"> <div class="flex justify-between">
<h2 class="h2-bold-bounded"> <h2 class="h2-bold-bounded">{{ $t('sign_up') }}</h2>
{{ $t("sign_up") }}
</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">{{
@click="menuStore.navigateToItem()" $t('back_to_home') }}</button>
>
{{ $t("back_to_home") }}
</button>
</div> </div>
<div class="space-y-[25px] sm:space-y-[30px]"> <div class="space-y-[25px] sm:space-y-[30px]">
<p>{{ $t("current_information") }}</p> <p>Obecné informace</p>
<div class="grid grid-cols-1 md: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"> <p class="pl-6">{{ $t('first_name') }}</p>
{{ $t("first_name") }} <input :placeholder="$t('first_name')" type="text"
</p> 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" />
<input
:placeholder="$t('first_name')"
type="text"
class="text-sm sm:text-xl border 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"> <p class="pl-6">{{ $t('last_name') }}</p>
{{ $t("last_name") }} <input :placeholder="$t('last_name')" type="text"
</p> 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" />
<input
:placeholder="$t('last_name')"
type="text"
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"> <p class="pl-6">{{ $t('email') }}</p>
{{ $t("email") }} <input :placeholder="$t('email')" type="text"
</p> 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" />
<input
:placeholder="$t('email')"
type="text"
class="text-sm sm:text-xl border 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 ref="dropdownRef" <div class="space-y-[15px]" ref="dropdownRef">
class="space-y-[15px]" <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" 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-sm sm:text-xl 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">
> <span :class="[dropCountry && 'rotate-180', 'transition-all']"> <i
<span class="uil uil-angle-down text-2xl font-light cursor-pointer"></i></span>
:class="[dropCountry && 'rotate-180', 'transition-all']"
>
<i
class="uil uil-angle-down text-2xl font-light cursor-pointer"
/></span>
<p class="text-sm sm:text-xl"> <p class="text-sm sm:text-xl">
{{ menuStore.selectedPhoneCountry.iso_code }} {{ menuStore.selectedPhoneCountry.iso_code }}
</p> </p>
</div> </div>
</div> </div>
<div <div v-if="dropCountry"
v-if="dropCountry" 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">
class="mt-2 absolute bg-bg-light dark:bg-bg-dark rounded-[5px] ring-0 cursor-pointer w-[130px] sm:w-full border border-button py-[10px] px-[5px] overflow-hidden"
>
<div class="overflow-y-auto h-[200px] w-full"> <div class="overflow-y-auto h-[200px] w-full">
<p <p v-for="item in menuStore.countries" :key="item.iso_code"
v-for="item in menuStore.countries" @click="() => { menuStore.selectedPhoneCountry = item; dropCountry = false }"
:key="item.iso_code"
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]" 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" :title="item.name">
@click="
() => {
menuStore.selectedPhoneCountry = item;
dropCountry = false;
}
"
>
{{ item.name }} {{ item.name }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<p class="text-sm sm:text-xl font-normal"> <p class="text-sm sm:text-xl font-normal">{{ menuStore.selectedPhoneCountry.call_prefix
{{ menuStore.selectedPhoneCountry.call_prefix }} }}</p>
</p> <input :placeholder="$t('phone')" type="text"
<input 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" />
id="phone"
:placeholder="$t('phone')"
type="text"
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"> <p class="pl-6">{{ $t('account_type') }}</p>
{{ $t("account_type") }} <USelect v-model="selectedType" :items="component.front_section_lang[0].data.account_types"
</p> value-key="name" :searchable="false" :ui="{
<USelect
v-model="selectedType"
:items="
component.front_section_lang
&& component.front_section_lang[0].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', 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', trailing: 'hidden w-full',
viewport: 'ring-0 min-w-full', viewport: 'ring-0 min-w-full',
content: content: 'bg-bg-light dark:bg-bg-dark ring-0 border border-button',
'bg-bg-light dark:bg-bg-dark ring-0 border border-button',
leading: leading:
'left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 p-0 w-full', 'left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 p-0 w-full',
group: 'px-[5px] py-[10px]', 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', 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 }"> <template #leading="{ modelValue }">
<div <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]" 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"> <p class="truncate whitespace-nowrap">
{{ {{ component.front_section_lang[0].data.account_types.find((item) => item.name === modelValue)?.name }}
component.front_section_lang
&& component.front_section_lang[0].data.account_types.find(
(item) => item.name === modelValue,
)?.name
}}
</p> </p>
<span> <span> <i
<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"
/></span>
</div> </div>
</template> </template>
<template #item="{ item }"> <template #item="{ item }">
<div <div class="flex items-center gap-2 cursor-pointer min-w-full">
class="flex items-center gap-2 cursor-pointer min-w-full"
>
<p <p
class="truncate whitespace-nowrap text-sm sm:text-xl font-medium uppercase text-text-light dark:text-text-dark opacity-100" class="truncate whitespace-nowrap text-sm sm:text-xl font-medium uppercase text-text-light dark:text-text-dark opacity-100">
>
{{ item.name }} {{ item.name }}
</p> </p>
</div> </div>
@ -172,46 +101,30 @@
</USelect> </USelect>
</div> </div>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">{{ $t('partner_code') }}</p>
{{ $t("partner_code") }} <input :placeholder="$t('placeholder_password')" type="text"
</p> 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" />
<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> </div>
</div> </div>
</div> </div>
</div> </div>
<div <div class="py-[25px] sm:py-12 border-b border-gray flex justify-center w-full">
class="py-[25px] sm:py-12 border-b border-gray flex justify-center w-full" <UiButtonArrow type="fill" :arrow="true">{{ $t('login') }}</UiButtonArrow>
>
<UiButtonArrow type="fill"
:arrow="true"
>
{{
$t("sign_up")
}}
</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("is_account") }} $t('is_account')
</p> }}</p>
<nuxt-link :to="{ name: `id-slug___${$i18n.locale}`, params: { id: menuStore.getLoginMenu()?.id, slug: menuStore.getLoginMenu()?.front_menu_lang.at(0)?.link_rewrite } }"> <p class="text-button cursor-pointer hover:text-button-hover">{{
<p class="text-button cursor-pointer hover:text-button-hover"> $t('login')
{{ $t("login") }} }}</p>
</p>
</nuxt-link>
</div> </div>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from "@vueuse/core";
const props = defineProps<{ const props = defineProps<{
component: { component: {
id: number id: number
@ -223,7 +136,7 @@ const props = defineProps<{
front_section_lang: { front_section_lang: {
data: { data: {
account_types: { account_types: {
id: number id: number,
name: string name: string
}[] }[]
} }
@ -231,18 +144,15 @@ const props = defineProps<{
id_lang: number id_lang: number
}[] }[]
} }
}>() }>();
const menuStore = useMenuStore() const menuStore = useMenuStore()
const dropdownRef = ref(null) const dropdownRef = ref(null);
const dropCountry = ref() const dropCountry = ref()
const selectedType = ref() const selectedType = ref(props.component.front_section_lang[0].data.account_types[0].name)
if (props.component.front_section_lang)
selectedType.value
= props.component.front_section_lang[0].data.account_types[0].name
onClickOutside(dropdownRef, () => { onClickOutside(dropdownRef, () => {
dropCountry.value = false dropCountry.value = false
}) });
</script> </script>

View File

@ -1,62 +1,41 @@
<template> <template>
<UiContainer class="flex py-10 sm:py-14 gap-10"> <UiContainer class="flex py-10 sm:py-14 gap-10">
<div <div class="hidden xl:block rounded-2xl min-w-[50%] h-[830px]" :style="{
class="hidden xl:block rounded-2xl min-w-[50%] 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',
backgroundPosition: 'center', backgroundPosition: 'center',
}" }" />
/> <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-y-[55px]"> <div class="space-y-[55px]">
<div <div class="flex flex-col-reverse sm:flex-row justify-between items-start gap-7 sm:items-center">
class="flex flex-col-reverse sm:flex-row justify-between items-start gap-7 sm:items-center" <h2 class="h2-bold-bounded">{{ $t('reset_password') }}</h2>
>
<h2 class="h2-bold-bounded">
{{ $t("reset_password") }}
</h2>
<button <button
class="whitespace-nowrap 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="whitespace-nowrap 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-[30px]">
<p>{{ component.section_lang_data.reset_password_description }}</p> <p>{{ component.section_lang_data.reset_password_description }}</p>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p class="pl-6"> <p class="pl-6">{{ $t('email') }}</p>
{{ $t("email") }} <input :placeholder="$t('email')" type="text"
</p> 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" />
<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"
>
</div> </div>
</div> </div>
</div> </div>
<div class="pt-[25px] sm:pt-12 flex justify-center w-full"> <div class="pt-[25px] sm:pt-12 flex justify-center w-full">
<UiButtonArrow type="fill" <UiButtonArrow type="fill" :arrow="true">{{ $t('confirm') }}</UiButtonArrow>
:arrow="true"
>
{{
$t("confirm")
}}
</UiButtonArrow>
</div> </div>
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineProps<{ component: Component }>() 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: {
reset_password_description: string reset_password_description: string
} };
} };</script>
</script>

View File

@ -1,57 +1,94 @@
<template> <template>
<!-- <SectionShopPageCurrencyRatesBar <SectionShopPageCurrencyRatesBar
class="mb-[25px] sm:mb-[55px] xl:mb-[75px]" class="mb-[25px] sm:mb-[55px] xl:mb-[75px]"
/> --> />
<UiContainer> <UiContainer>
<div class="flex flex-col gap-[25px] sm:gap-10 xl:flex-row"> <div class="flex flex-col gap-[25px] sm:gap-10 xl:flex-row">
<!-- button to open categories --> <!-- button to open categories -->
<div class="xl:hidden flex items-center w-full"> <div class="xl:hidden flex items-center w-full">
<button <button
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" 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"
@click="openCategories = !openCategories"> @click="openCategories = !openCategories"
>
Otevřené kategorie a filtry Otevřené kategorie a filtry
</button> </button>
</div> </div>
<Transition> <Transition>
<div v-if="openCategories" class="min-w-[250px] px-5 sm:p-0 xl:hidden"> <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]"> <h1
class="font-bounded leading-[140%] font-bold text-[24px] mb-[25px]"
>
{{ $t("category") }} {{ $t("category") }}
</h1> </h1>
<div class="flex flex-col gap-[25px]"> <div class="flex flex-col gap-[25px]">
<div> <div>
<div v-if="categoriesList && categoriesList.length < 1" class="animate-pulse"> <div
<div class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24"> v-if="categoriesList && 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 class="w-32 h-4 bg-gray-200 rounded" />
<div class="w-4 h-4 bg-gray-200 rounded-full" /> <div class="w-4 h-4 bg-gray-200 rounded-full" />
</div> </div>
</div> </div>
<CategoryTree :data="categoriesList" :active="categoryId" @change-category="changeCategory($event)" /> <CategoryTree
:data="categoriesList"
:active="categoryId"
@change-category="changeCategory($event)"
/>
</div> </div>
<div> <div>
<p class="mb-[25px] text-lg font-extrabold text-black dark:text-white"> <p
class="mb-[25px] text-lg font-extrabold text-black dark:text-white"
>
{{ $t("filtered_by") }} {{ $t("filtered_by") }}
</p> </p>
<div v-for="(item, itemIndex) in filters" :key="itemIndex" :class="[ <div
v-for="(item, itemIndex) in filters"
:key="itemIndex"
:class="[
'mb-[30px]', 'mb-[30px]',
visibleFeatures[item.feature] && 'border-b border-block pb-2', 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)"> <span
class="flex justify-between items-center font-bold cursor-pointer mb-[25px] text-base"
@click="toggleFeature(item.feature)"
>
{{ item.feature }} {{ item.feature }}
<span :class="[ <span
:class="[
visibleFeatures[item.feature] && 'rotate-180', visibleFeatures[item.feature] && 'rotate-180',
'transition-all', 'transition-all',
]"><i class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto" /></span> ]"
><i
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"
/></span>
</span> </span>
<ul v-show="visibleFeatures[item.feature]" class="flex flex-col gap-5"> <ul
<li v-for="filter in item.feature_values" :key="filter.value_id" v-show="visibleFeatures[item.feature]"
class="flex items-center gap-[10px] cursor-pointer"> class="flex flex-col gap-5"
<input :id="`${filter.value_id}`" v-model="selectedFilters" >
:value="`${filter.parent}.${filter.value_id}`" type="checkbox" class="border-button !bg-inherit"> <li
<label :for="`${filter.value_id}`" v-for="filter in item.feature_values"
class="cursor-pointer flex items-center justify-between w-full text-base"> :key="filter.value_id"
class="flex items-center gap-[10px] cursor-pointer"
>
<input
:id="`${filter.value_id}`"
v-model="selectedFilters"
:value="`${filter.parent}.${filter.value_id}`"
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>{{ filter.value }}</span>
<span>12</span> <span>12</span>
</label> </label>
@ -70,13 +107,22 @@
</h1> </h1>
<div class="flex flex-col gap-12"> <div class="flex flex-col gap-12">
<div> <div>
<div v-if="categoriesList && categoriesList.length < 1" class="animate-pulse"> <div
<div class="flex items-center justify-between mt-4 text-white rounded-lg cursor-pointer xl:pr-24"> v-if="categoriesList && 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 class="w-32 h-4 bg-gray-200 rounded" />
<div class="w-4 h-4 bg-gray-200 rounded-full" /> <div class="w-4 h-4 bg-gray-200 rounded-full" />
</div> </div>
</div> </div>
<CategoryTree :data="categoriesList" :active="categoryId" @change-category="changeCategory($event)" /> <CategoryTree
:data="categoriesList"
:active="categoryId"
@change-category="changeCategory($event)"
/>
</div> </div>
<div> <div>
<p class="mb-10 text-2xl font-extrabold text-black dark:text-white"> <p class="mb-10 text-2xl font-extrabold text-black dark:text-white">
@ -91,28 +137,52 @@
</div> </div>
</div> --> </div> -->
<div v-for="(item, itemIndex) in filters" :key="itemIndex" :class="[ <div
v-for="(item, itemIndex) in filters"
:key="itemIndex"
:class="[
'mb-[30px]', 'mb-[30px]',
visibleFeatures[item.feature] && 'border-b border-block pb-2', 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)"> <span
class="flex justify-between items-center font-bold cursor-pointer mb-[25px]"
@click="toggleFeature(item.feature)"
>
{{ item.feature }} {{ item.feature }}
<span :class="[ <span
:class="[
visibleFeatures[item.feature] && 'rotate-180', visibleFeatures[item.feature] && 'rotate-180',
'transition-all', 'transition-all',
]"><i class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto" /></span> ]"
><i
class="iconify i-lucide:chevron-down text-button shrink-0 size-6 ms-auto"
/></span>
</span> </span>
<ul v-show="visibleFeatures[item.feature]" class="flex flex-col gap-5"> <ul
<li v-for="filter in item.feature_values" :key="filter.value_id" v-show="visibleFeatures[item.feature]"
class="flex items-center gap-[10px] cursor-pointer"> 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}`" <!-- <input :id="`${filter.value_id}`" :value="`${filter.parent}.${filter.value_id}`"
v-model="selectedFilters" type="checkbox" class="border-button !bg-inherit" /> v-model="selectedFilters" type="checkbox" class="border-button !bg-inherit" />
<label :for="`${filter.value_id}`" class="cursor-pointer">{{ filter.value }}</label> --> <label :for="`${filter.value_id}`" class="cursor-pointer">{{ filter.value }}</label> -->
<input :id="`${filter.value_id}`" v-model="selectedFilters" <input
:value="`${filter.parent}.${filter.value_id}`" type="checkbox" class="border-button !bg-inherit"> :id="`${filter.value_id}`"
<label :for="`${filter.value_id}`" class="cursor-pointer flex items-center justify-between w-full"> v-model="selectedFilters"
:value="`${filter.parent}.${filter.value_id}`"
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>{{ filter.value }}</span>
<span>12</span> <span>12</span>
</label> </label>
@ -125,30 +195,52 @@
<div class="w-full space-y-10"> <div class="w-full space-y-10">
<!-- pop-up --> <!-- pop-up -->
<div v-if="isInfo" <div
class="w-full xl:w-[70%] mx-auto border-y border-block py-[15px] sm:p-[30px] flex gap-[55px] relative"> v-if="isInfo"
<UButton variant="ghost" class="w-full xl:w-[70%] mx-auto border-y border-block py-[15px] sm:p-[30px] flex gap-[55px] relative"
>
<UButton
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" 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"
size="xl" icon="i-lucide-x" @click="closeElement()" /> size="xl"
icon="i-lucide-x"
@click="closeElement()"
/>
<div class="flex flex-col sm:flex-row gap-[25px]"> <div class="flex flex-col sm:flex-row gap-[25px]">
<div class="flex flex-col justify-between 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"> <h4
class="font-inter text-lg sm:text-[24px] leading-[150%] md:leading-[120%] font-bold"
>
{{ component.front_section_lang[0].data.title }} {{ component.front_section_lang[0].data.title }}
</h4> </h4>
<p>{{ component.front_section_lang[0].data.description }}</p> <p>{{ component.front_section_lang[0].data.description }}</p>
</div> </div>
<img class="max-w-[150px] mx-auto" :src="`/api/public/file/${component.img[0]}_m.webp')`"> <img
class="max-w-[150px] mx-auto"
:src="`/api/public/file/${component.img[0]}_m.webp')`"
/>
</div> </div>
</div> </div>
<div v-if="products.length < 1" class="grid gap-12 pt-32 pb-16 md:grid-cols-2 2xl:grid-cols-3"> <div
<!-- <TheProductSkeleton v-for="index in 6" :key="index" /> --> 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" :key="index" />
</div> </div>
<!-- products --> <!-- products -->
<div v-else ref="loadingElement" class="flex flex-wrap justify-center gap-5 sm:gap-10"> <div
<Product v-for="product in products" :key="product.id" :product="product" /> 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>
<div v-if="reachedEnd" class="w-full flex justify-center"> <div v-if="reachedEnd" class="w-full flex justify-center">
@ -160,311 +252,296 @@
</div> </div>
</UiContainer> </UiContainer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue";
import Product from './Product.vue' import Product from "./Product.vue";
import CategoryTree from './CategoryTree.vue'
import type { import type {
Feature, Feature,
GenericResponse, GenericResponse,
GenericResponseChildren, GenericResponseChildren,
GenericResponseItems, GenericResponseItems,
ProductType, ProductType,
} from '~/types' } from "~/types";
import CategoryTree from "./CategoryTree.vue";
const { $session } = useNuxtApp() const { $session } = useNuxtApp();
watch( watch(
() => $session.cookieData, () => $session.cookieData,
async () => await getProducts(), async () => await getProducts(),
{ deep: true }, { deep: true }
) );
defineProps<{ defineProps<{
component: { component: {
id: number id: number;
name: string name: string;
img: string[] img: string[];
component_name: string component_name: string;
is_no_lang: boolean is_no_lang: boolean;
page_name: string page_name: string;
front_section_lang: { front_section_lang: {
data: { data: {
title: string title: string;
description: string description: string;
} };
id_front_section: number id_front_section: number;
id_lang: number id_lang: number;
}[] }[];
} };
}>() }>();
const openCategories = ref(false) const openCategories = ref(false);
const isInfo = ref<boolean>(true) const isInfo = ref<boolean>(true);
const selectedFilters = ref<string[]>([]) const selectedFilters = ref<any>([]);
const categoryId = ref<number>(1) const categoryId = ref<number>(1);
const loading = ref(false) const loading = ref(false);
const reachedEnd = ref(false) const reachedEnd = ref(false);
const loadingElement = ref<HTMLElement | null>(null) const loadingElement = ref<HTMLElement | null>(null);
const page = ref(1) const page = ref(1);
const elems = ref(12) const elems = ref(12);
const maxElements = ref(0) const maxElements = ref(0);
const products = ref([] as ProductType[]) const products = ref([] as ProductType[]);
async function getProducts() { async function getProducts() {
try { try {
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>( const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
`/api/public/products/category/${categoryId.value}?p=${page.value}&elems=${elems.value}`, `/api/public/products/category/${categoryId.value}?p=${page.value}&elems=${elems.value}`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
},
)
products.value = data.items
maxElements.value = data.items_count + 1
} }
catch (error) { );
console.error('getProducts error:', error)
products.value = data.items;
maxElements.value = data.items_count + 1;
} catch (error) {
console.error("getProducts error:", error);
} }
} }
const filters = ref([] as Feature[]) const filters = ref([] as Feature[]);
async function getCategory() { async function getCategory() {
try { try {
const { data } = await useMyFetch<GenericResponse<object>>( const { data } = await useMyFetch<GenericResponse<object>>(
`/api/public/products/category/1/classification`, `/api/public/products/category/1/classification`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
},
)
filters.value = data as Feature[]
filters.value.forEach((el: Feature) => {
const parentId = el.feature_id
el.feature_values.forEach((el) => {
el.parent = parentId
})
})
} }
catch (error) { );
console.error('getCategory error:', error)
filters.value = data as Feature[];
filters.value.forEach((el: Feature) => {
const parentId = el.feature_id;
el.feature_values.forEach((el) => {
el.parent = parentId;
});
});
} catch (error) {
console.error("getCategory error:", error);
} }
} }
const categoriesList = ref() const categoriesList = ref();
async function getCategoryTree() { async function getCategoryTree() {
try { try {
const { data } = await useMyFetch<GenericResponseChildren<ProductType[]>>( const { data } = await useMyFetch<GenericResponseChildren<ProductType[]>>(
`/api/public/categories/tree`, `/api/public/categories/tree`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
},
)
categoriesList.value = data.children
} }
catch (error) { );
console.error('getCategory error:', error)
categoriesList.value = data.children;
} catch (error) {
console.error("getCategory error:", error);
} }
} }
getProducts() getProducts();
getCategory() getCategory();
getCategoryTree() getCategoryTree();
const closeElement = () => { const closeElement = () => {
isInfo.value = false isInfo.value = false;
} };
onMounted(() => { onMounted(() => {
window.addEventListener('scroll', scrollEvent) window.addEventListener("scroll", scrollEvent);
}) });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('scroll', scrollEvent) window.removeEventListener("scroll", scrollEvent);
}) });
async function scrollEvent() { async function scrollEvent() {
const maxScrollY const maxScrollY =
= window.scrollY window.scrollY ||
|| document.documentElement.scrollHeight document.documentElement.scrollHeight -
- document.documentElement.clientHeight document.documentElement.clientHeight;
if ( if (
window.scrollY >= maxScrollY - 500 window.scrollY >= maxScrollY - 500 &&
&& !reachedEnd.value !reachedEnd.value &&
&& !loading.value !loading.value
) { ) {
loading.value = true loading.value = true;
await loadMoreProducts() await loadMoreProducts();
loading.value = false loading.value = false;
} }
} }
filters.value.forEach((item) => { filters.value.forEach((item) => {
visibleFeatures[item.feature] = false visibleFeatures[item.feature] = false;
}) });
const visibleFeatures = reactive<Record<string, boolean>>({}) const visibleFeatures = reactive<Record<string, boolean>>({});
function toggleFeature(feature: string) { function toggleFeature(feature: string) {
if (feature in visibleFeatures) { if (feature in visibleFeatures) {
visibleFeatures[feature] = !visibleFeatures[feature] visibleFeatures[feature] = !visibleFeatures[feature];
} } else {
else { visibleFeatures[feature] = true;
visibleFeatures[feature] = true
} }
} }
class FilteredQueryString extends URLSearchParams { class FilteredQueryString extends URLSearchParams {
override append(name: string, value: string): void { override append(name: string, value: string): void {
if (value == null) { if (value == null) {
return return;
} }
super.append(name, value) super.append(name, value);
} }
} }
async function loadMoreProducts() { async function loadMoreProducts() {
const qParams = new FilteredQueryString() const qParams = new FilteredQueryString();
page.value = page.value + 1 page.value = page.value + 1;
qParams.append('p', `${page.value}`) qParams.append("p", `${page.value}`);
qParams.append('elems', `${elems.value}`) qParams.append("elems", `${elems.value}`);
qParams.append( qParams.append(
'features', "features",
selectedFilters.value.length > 0 ? selectedFilters.value : null, selectedFilters.value.length > 0 ? selectedFilters.value : null
) );
try { try {
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>( const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
`/api/public/products/category/${categoryId.value}?${qParams.toString()}`, `/api/public/products/category/${categoryId.value}?${qParams.toString()}`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
maxElements.value = data.items_count maxElements.value = data.items_count;
if (data.items) { if (data.items) {
products.value.push(...(data.items as ProductType[])) products.value.push(...(data.items as ProductType[]));
} } else {
else { reachedEnd.value = true;
reachedEnd.value = true
} }
if (products.value.length >= maxElements.value) { if (products.value.length >= maxElements.value) {
reachedEnd.value = true reachedEnd.value = true;
} }
} } catch (error) {
catch (error) { console.error("getCategory error:", error);
console.error('getCategory error:', error)
} }
} }
interface CategoryItem { const changeCategory = (item: any) => {
id: number categoryId.value = item.id;
name: string };
}
const changeCategory = (item: CategoryItem) => {
categoryId.value = item.id
}
watch(selectedFilters, async (newQuestion: string) => { watch(selectedFilters, async (newQuestion: string) => {
if (newQuestion) { if (newQuestion) {
page.value = 1 page.value = 1;
reachedEnd.value = false reachedEnd.value = false;
loadingElement.value?.scrollIntoView() loadingElement.value?.scrollIntoView();
const qParams = new FilteredQueryString() const qParams = new FilteredQueryString();
qParams.append('p', `${page.value}`) qParams.append("p", `${page.value}`);
qParams.append('elems', `${elems.value}`) qParams.append("elems", `${elems.value}`);
qParams.append( qParams.append(
'features', "features",
selectedFilters.value.length > 0 ? selectedFilters.value : null, selectedFilters.value.length > 0 ? selectedFilters.value : null
) );
try { try {
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>( const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
`/api/public/products/category/1?${qParams.toString()}`, `/api/public/products/category/1?${qParams.toString()}`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
products.value = data.items products.value = data.items;
maxElements.value = data.items_count maxElements.value = data.items_count;
} } catch (error) {
catch (error) { console.error("selectedFilters error:", error);
console.error('selectedFilters error:', error)
} }
} }
}) });
watch(categoryId, async (newCategoryId) => { watch(categoryId, async (newCategoryId) => {
if (newCategoryId) { if (newCategoryId) {
page.value = 1 page.value = 1;
reachedEnd.value = false reachedEnd.value = false;
loadingElement.value?.scrollIntoView() loadingElement.value?.scrollIntoView();
const qParams = new FilteredQueryString() const qParams = new FilteredQueryString();
qParams.append('p', `${page.value}`) qParams.append("p", `${page.value}`);
qParams.append('elems', `${elems.value}`) qParams.append("elems", `${elems.value}`);
qParams.append( qParams.append(
'features', "features",
selectedFilters.value.length > 0 ? selectedFilters.value : null, selectedFilters.value.length > 0 ? selectedFilters.value : null
) );
try { try {
const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>( const { data } = await useMyFetch<GenericResponseItems<ProductType[]>>(
`api/public/products/category/${newCategoryId}?${qParams.toString()}`, `api/public/products/category/${newCategoryId}?${qParams.toString()}`,
{ {
headers: { 'Content-Type': 'application/json' }, headers: { "Content-Type": "application/json" },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
products.value = data.items products.value = data.items;
maxElements.value = data.items_count maxElements.value = data.items_count;
} } catch (error) {
catch (error) { console.error("getCategory error:", error);
console.error('getCategory error:', error)
} }
} }
}) });
</script> </script>
<style scoped> <style scoped>

View File

@ -1,41 +1,26 @@
<template> <template>
<div <div class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap">
class="group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap" <button :class="[
>
<button
:class="[
'h-[40px] cursor-pointer min-w-40 rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px]', 'h-[40px] cursor-pointer min-w-40 rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px]',
type === 'fill' type === 'fill'
? 'bg-button text-text-dark group-hover:bg-button-hover' ? 'bg-button text-text-dark group-hover:bg-button-hover'
: type === 'border' : type === 'border'
? 'border-button text-button group-hover:border-button-hover group-hover:text-button-hover border' ? 'border-button text-button group-hover:border-button-hover group-hover:text-button-hover border'
: 'border-button text-button dark:border-block dark:text-block group-hover:border-button-hover group-hover:text-button-hover border', : 'border-button text-button dark:border-block dark:text-block group-hover:border-button-hover group-hover:text-button-hover border',
full && 'w-full', full && 'w-full'
]" ]">
>
<slot /> <slot />
</button> </button>
<div <div v-if="arrow" :class="[
v-if="arrow"
:class="[
'flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px]', 'flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px]',
type === 'fill' type === 'fill'
? 'bg-button text-text-dark group-hover:bg-button-hover' ? 'bg-button text-text-dark group-hover:bg-button-hover'
: 'border-button text-button dark:border-block dark:text-block group-hover:border-button-hover group-hover:text-button-hover border', : 'border-button text-button dark:border-block dark:text-block group-hover:border-button-hover group-hover:text-button-hover border',
]" ]">
> <svg class="" width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
class=""
width="26"
height="26"
viewBox="0 0 26 26"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z" d="M25.1274 1.87258C25.1274 1.3203 24.6797 0.872582 24.1274 0.872584L15.1274 0.872583C14.5751 0.872583 14.1274 1.3203 14.1274 1.87258C14.1274 2.42487 14.5751 2.87258 15.1274 2.87258L23.1274 2.87258L23.1274 10.8726C23.1274 11.4249 23.5751 11.8726 24.1274 11.8726C24.6797 11.8726 25.1274 11.4249 25.1274 10.8726L25.1274 1.87258ZM1.5 24.5L2.20711 25.2071L24.8345 2.57969L24.1274 1.87258L23.4203 1.16548L0.792893 23.7929L1.5 24.5Z"
fill="currentColor" fill="currentColor" />
/>
</svg> </svg>
</div> </div>
</div> </div>
@ -52,5 +37,5 @@ defineProps({
full: { full: {
type: Boolean, type: Boolean,
}, },
}) });
</script> </script>

View File

@ -1,46 +1,23 @@
<template> <template>
<div class="space-y-[15px]"> <div class="space-y-[15px]">
<p :for="`base-input-${id}`" <p :for="`base-input-${id}`" class="pl-6">
class="pl-6"
>
<slot /> <slot />
</p> </p>
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex relative"> <div class="flex relative">
<input <input :id="`base-input-${id}`" :value="modelValue" :type="!isPasswordVisible ? type : 'text'"
:id="`base-input-${id}`" :placeholder="placeholder" :disabled="disabled"
:value="modelValue" @input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
:type="!isPasswordVisible ? type : 'text'" @focus="$emit('focus')" @blur="$emit('blur')"
:placeholder="placeholder" class="border border-block placeholder:text-gray dark:placeholder:text-button-disabled rounded-lg px-6 h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" />
:disabled="disabled" <i v-if="disabled"
class="border border-block placeholder:text-gray dark:placeholder:text-button-disabled rounded-lg px-6 h-[50px] sm:h-[67px] w-full focus:outline-none focus:ring-0 focus:border-2" class="uil uil-lock-alt text-[22px] absolute right-6 top-1/2 -translate-y-1/2 text-gray" />
@input="
$emit(
'update:modelValue',
($event.target as HTMLInputElement).value,
)
"
@focus="$emit('focus')"
@blur="$emit('blur')"
>
<i
v-if="disabled"
class="uil uil-lock-alt text-[22px] absolute right-6 top-1/2 -translate-y-1/2 text-gray"
/>
<div <div v-if="type === 'password'" class="order-2 ml-1.5 cursor-pointer" :title="!isPasswordVisible ? $t('show_password') : $t('hide_password')
v-if="type === 'password'" " @click="isPasswordVisible = !isPasswordVisible">
class="order-2 ml-1.5 cursor-pointer" <FaceObserver class="ml-4 text-xl leading-6" :isPasswordVisible="isPasswordVisible" />
:title="
!isPasswordVisible ? $t('show_password') : $t('hide_password')
"
@click="isPasswordVisible = !isPasswordVisible"
>
<FaceObserver
class="ml-4 text-xl leading-6"
:is-password-visible="isPasswordVisible"
/>
</div> </div>
</div> </div>
<!-- <p class="mt-2 text-xs text-red-600">{{ validationText }}</p> --> <!-- <p class="mt-2 text-xs text-red-600">{{ validationText }}</p> -->
@ -51,26 +28,25 @@
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import FaceObserver from './FaceObserver.vue' import FaceObserver from './FaceObserver.vue';
defineEmits(['update:modelValue', 'focus', 'blur']) defineEmits(["update:modelValue", "focus", "blur"]);
defineProps<{ defineProps<{
modelValue?: string | undefined modelValue?: string | any;
modelModifiers?: object modelModifiers?: object;
id: number id: number;
type?: string type?: string;
disabled?: boolean disabled?: boolean;
placeholder?: string placeholder?: string;
validation?: boolean | null validation?: boolean | null;
validationText?: string validationText?: string;
}>() }>();
const isPasswordVisible = ref(false);
const isPasswordVisible = ref(false)
</script> </script>
<style> <style>
input:-webkit-autofill, input:-webkit-autofill,
input:-webkit-autofill:hover, input:-webkit-autofill:hover,

View File

@ -1,7 +1,6 @@
<template> <template>
<UContainer <UContainer
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" 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>

View File

@ -1,37 +1,15 @@
<template> <template>
<svg <svg v-if="isPasswordVisible" xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 -960 960 960" width="16px" fill="#000000"><path d="m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z"/></svg>
v-if="isPasswordVisible" <svg v-else xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 -960 960 960" width="16px" fill="#000000"><path d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"/></svg>
xmlns="http://www.w3.org/2000/svg" </template>
height="16px"
viewBox="0 -960 960 960"
width="16px"
fill="#000000"
>
<path
d="m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z"
/>
</svg>
<svg
v-else
xmlns="http://www.w3.org/2000/svg"
height="16px"
viewBox="0 -960 960 960"
width="16px"
fill="#000000"
>
<path
d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"
/>
</svg>
</template>
<script> <script>
export default { export default {
props: { props: {
isPasswordVisible: { isPasswordVisible: {
type: Boolean, type: Boolean,
required: true, required: true
}, }
}, }
} };
</script> </script>

View File

@ -1,45 +1,23 @@
<template> <template>
<div class="hidden md:block"> <div class="hidden md:block">
<svg <svg width="100%" height="100%" viewBox="0 0 870 350" fill="none" xmlns="http://www.w3.org/2000/svg">
width="100%"
height="100%"
viewBox="0 0 870 350"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs> <defs>
<clipPath id="customClip"> <clipPath id="customClip">
<path <path
d="M20 0.5H847.666C858.366 0.5 867.067 9.12193 867.165 19.8213L869.315 254.821C869.415 265.66 860.656 274.5 849.816 274.5H653C641.678 274.5 632.5 283.678 632.5 295V330C632.5 340.77 623.77 349.5 613 349.5H20C9.23045 349.5 0.5 340.77 0.5 330V20C0.5 9.23045 9.23045 0.5 20 0.5Z" d="M20 0.5H847.666C858.366 0.5 867.067 9.12193 867.165 19.8213L869.315 254.821C869.415 265.66 860.656 274.5 849.816 274.5H653C641.678 274.5 632.5 283.678 632.5 295V330C632.5 340.77 623.77 349.5 613 349.5H20C9.23045 349.5 0.5 340.77 0.5 330V20C0.5 9.23045 9.23045 0.5 20 0.5Z" />
/>
</clipPath> </clipPath>
</defs> </defs>
<image <image :href="src" clip-path="url(#customClip)" preserveAspectRatio="xMidYMid slice" width="100%"
:href="src" height="100%" />
clip-path="url(#customClip)"
preserveAspectRatio="xMidYMid slice"
width="100%"
height="100%"
/>
<foreignObject <foreignObject x="640" y="285" width="calc(100% - 640px - 1px)" height="calc(100% - 285px)">
x="640"
y="285"
width="calc(100% - 640px - 1px)"
height="calc(100% - 285px)"
>
<slot name="button" /> <slot name="button" />
</foreignObject> </foreignObject>
</svg> </svg>
</div> </div>
<div class="block md:hidden"> <div class="block md:hidden">
<img <img :src="src" width="100%" height="100%" class="object-contain rounded-2xl my-4" />
:src="src"
width="100%"
height="100%"
class="object-contain rounded-2xl my-4"
>
<div class="flex justify-center"> <div class="flex justify-center">
<slot name="button" /> <slot name="button" />
</div> </div>

View File

@ -1,9 +1,9 @@
import { ofetch } from 'ofetch' import { ofetch } from "ofetch";
export interface RequestOptions<T> extends RequestInit { export interface RequestOptions<T> extends RequestInit {
onErrorOccured?: (error: Error, statusCode: number) => void onErrorOccured?: (error: Error, statusCode: number) => void;
onSuccess?: (data: T, statusCode: number) => void onSuccess?: (data: T, statusCode: number) => void;
onStart?: () => void onStart?: () => void;
} }
/** /**
@ -23,52 +23,50 @@ export interface RequestOptions<T> extends RequestInit {
*/ */
export const useMyFetch = async <T>( export const useMyFetch = async <T>(
url: string, url: string,
options?: RequestOptions<T>, options?: RequestOptions<T>
): Promise<T> => { ): Promise<T> => {
if (options?.onStart) options.onStart() if (options?.onStart) options.onStart();
let response = null let response = null;
try { try {
const event = useRequestEvent() const event = useRequestEvent();
if (options == null) options = {} if (options == null) options = {};
options.credentials = 'include' options.credentials = "include";
if (import.meta.server) { if (import.meta.server) {
const api_uri const api_uri =
= event?.node.req.headers['api-uri'] || 'http://localhost:4000' event?.node.req.headers["api-uri"] || "http://localhost:4000";
url = api_uri + url url = api_uri + url;
options.headers = event?.headers options.headers = event?.headers;
} }
response = await ofetch.raw(url, options) response = await ofetch.raw(url, options);
if (import.meta.server && !event?.handled) { if (import.meta.server && !event?.handled) {
for (const cookie of response.headers.getSetCookie()) { for (const cookie of response.headers.getSetCookie()) {
event?.headers.set('Cookie', cookie) event?.headers.set("Cookie", cookie);
event?.node.res.setHeader('set-cookie', cookie) event?.node.res.setHeader("set-cookie", cookie);
} }
} }
// handle errors if any // handle errors if any
if (!response.ok && typeof options.onErrorOccured == 'function') { if (!response.ok && typeof options.onErrorOccured == "function") {
options.onErrorOccured(new Error(response.statusText), response.status) options.onErrorOccured(new Error(response.statusText), response.status);
} }
// handle success to be able clearly marked that request has finished // handle success to be able clearly marked that request has finished
if (response.ok && typeof options.onSuccess == 'function') { if (response.ok && typeof options.onSuccess == "function") {
options.onSuccess(response._data, response.status) options.onSuccess(response._data, response.status);
} }
return response._data as T return response._data as T;
} } catch (e) {
catch (e) {
// handle errors if any // handle errors if any
if (typeof options?.onErrorOccured == 'function') { if (typeof options?.onErrorOccured == "function") {
options.onErrorOccured(e as Error, response?.status || 500) options.onErrorOccured(e as Error, response?.status || 500);
} else {
console.error(e);
} }
else { return {} as T;
console.error(e)
} }
return {} as T };
}
}

View File

@ -1,14 +1,8 @@
<template> <template>
<div class="p-10 text-center"> <div class="p-10 text-center">
<h1 class="text-3xl font-bold"> <h1 class="text-3xl font-bold">Error {{ error?.statusCode }}</h1>
Error {{ error?.statusCode }} <p class="mt-4 text-gray-600">{{ error?.statusMessage }}</p>
</h1> <NuxtLink to="/" class="mt-6 text-blue-500 underline">Go back home</NuxtLink>
<p class="mt-4 text-gray-600">
{{ error?.statusMessage }}
</p>
<NuxtLink to="/"
class="mt-6 text-blue-500 underline"
>Go back home</NuxtLink>
</div> </div>
</template> </template>

View File

@ -1,21 +1,5 @@
// @ts-check // @ts-check
import { createConfigForNuxt } from '@nuxt/eslint-config' import withNuxt from "./.nuxt/eslint.config.mjs";
export default createConfigForNuxt({ export default withNuxt();
features: { // Your custom configs here
stylistic: true,
typescript: true,
},
}).override('nuxt/vue/rules', {
rules: {
'vue/first-attribute-linebreak': 'off',
'vue/no-v-html': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/html-self-closing': 'off',
'vue/max-attributes-per-line': 'off',
},
}).override('nuxt/stylistic', {
rules: {
'no-useless-escape': 'off',
},
})

3
i18n/locales/cs.json Normal file
View File

@ -0,0 +1,3 @@
{
"welcome": "Welcome to Nuxt 3"
}

View File

@ -1,11 +1,9 @@
<template> <template>
<div <div
class="bg-bg-light dark:bg-bg-dark text-text-light dark:text-text-dark font-inter flex min-h-[calc(100vh-50px)] flex-col overflow-hidden" class="bg-bg-light dark:bg-bg-dark text-text-light dark:text-text-dark font-inter flex min-h-screen flex-col overflow-hidden">
>
<HeaderBlock /> <HeaderBlock />
<div <div
class="flex py-[25px] sm:py-[55px] md:py-[75px] space-y-[55px] sm:space-y-[75px] md:space-y-[100px] text-inter min-h-[inherit] flex-col justify-between" class="flex-1 py-[25px] sm:py-[55px] md:py-[75px] space-y-[55px] sm:space-y-[75px] md:space-y-[100px] text-inter">
>
<slot /> <slot />
</div> </div>
<!-- <FooterBlock /> --> <!-- <FooterBlock /> -->
@ -14,7 +12,7 @@
<script setup lang="ts"> <script setup lang="ts">
// import FooterBlock from "~/components/section/FooterBlock.vue"; // import FooterBlock from "~/components/section/FooterBlock.vue";
// useHead({ useHead({
// link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.png" }], link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.png" }],
// }); });
</script> </script>

View File

@ -1,34 +1,47 @@
import tailwindcss from '@tailwindcss/vite' import tailwindcss from "@tailwindcss/vite";
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: "2024-11-01",
devtools: { enabled: false },
modules: [ modules: [
'@pinia/nuxt', "@pinia/nuxt",
'@nuxt/eslint', "@nuxt/eslint",
'@nuxt/ui', "@nuxt/ui",
'@nuxtjs/i18n', "@nuxtjs/i18n",
'@pinia/nuxt', "@pinia/nuxt",
], ],
devtools: { enabled: false },
i18n: {
locales: [
{ code: "pl", name: "Polski", icon: "circle-flags:pl" },
{ code: "en", name: "English", icon: "circle-flags:gb" },
{ code: "cs", name: "Čeština", icon: "circle-flags:cz" },
],
lazy: true,
defaultLocale: "en",
strategy: "prefix",
bundle: {
optimizeTranslationDirective: false,
},
},
css: [ css: [
'@/assets/main.css', "@/assets/main.css",
'vue3-toastify/dist/index.css', "vue3-toastify/dist/index.css",
'@/assets/toastify-custom.css', "@/assets/toastify-custom.css",
], ],
ui: {},
compatibilityDate: '2024-11-01',
vite: { vite: {
plugins: [tailwindcss()], plugins: [tailwindcss()],
build: { build: {
sourcemap: false, sourcemap: false,
}, },
server: { server: {
allowedHosts: ['arina.ma-al.pl', 'marek.ma-al.pl'], allowedHosts: ["arina.ma-al.pl", "marek.ma-al.pl"],
watch: { watch: {
ignored: ['**/backend/pb_data/**'], ignored: ["**/backend/pb_data/**"],
}, },
hmr: { hmr: {
host: '127.0.0.1', host: "127.0.0.1",
clientPort: 3000, // useful if proxying clientPort: 3000, // useful if proxying
}, },
}, },
@ -36,26 +49,13 @@ export default defineNuxtConfig({
typescript: { typescript: {
tsConfig: { tsConfig: {
compilerOptions: { compilerOptions: {
typeRoots: ['./types', './node_modules/@types'], typeRoots: ["./types", "./node_modules/@types"],
}, },
include: ['./types'], include: ["./types"],
},
},
i18n: {
locales: [
{ code: 'pl', name: 'Polski', icon: 'circle-flags:pl' },
{ code: 'en', name: 'English', icon: 'circle-flags:gb' },
{ code: 'cs', name: 'Čeština', icon: 'circle-flags:cz' },
],
lazy: true,
defaultLocale: 'en',
strategy: 'prefix',
bundle: {
optimizeTranslationDirective: false,
}, },
}, },
ui: {},
icon: { icon: {
localApiEndpoint: '/___nuxt_icon', localApiEndpoint: "/___nuxt_icon",
}, },
}) });

View File

@ -7,21 +7,20 @@
"dev": "nuxt dev --host 0.0.0.0", "dev": "nuxt dev --host 0.0.0.0",
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview --host 0.0.0.0", "preview": "nuxt preview --host 0.0.0.0",
"postinstall": "nuxt prepare", "postinstall": "nuxt prepare"
"lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@iconscout/unicons": "^4.2.0", "@iconscout/unicons": "^4.2.0",
"@nuxt/eslint": "^1.5.2", "@nuxt/eslint": "^1.4.1",
"@nuxt/ui": "^3.2.0", "@nuxt/ui": "^3.1.3",
"@nuxtjs/i18n": "^9.5.6", "@nuxtjs/i18n": "^9.5.4",
"@pinia/nuxt": "^0.11.1", "@pinia/nuxt": "^0.11.0",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.8",
"@vueuse/core": "^13.5.0", "@vueuse/core": "^13.3.0",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"nuxt": "^3.17.6", "nuxt": "^3.17.4",
"tailwindcss": "^4.1.11", "tailwindcss": "^4.1.7",
"vue": "^3.5.17", "vue": "^3.5.14",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
"vue3-toastify": "^0.2.8" "vue3-toastify": "^0.2.8"
}, },

View File

@ -1,11 +1,7 @@
<template> <template>
<KeepAlive> <KeepAlive>
<component <component :is="component.componentInstance" v-for="component in componentsList" :key="component.name"
:is="component.componentInstance" :component="component.component" />
v-for="component in componentsList"
:key="component.name"
:component="component.component"
/>
</KeepAlive> </KeepAlive>
</template> </template>
@ -16,7 +12,8 @@ import ScrollTrigger from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger) gsap.registerPlugin(ScrollTrigger)
watch(useColorMode(), (color) => { watch(useColorMode(), (color) => {
console.log(color) console.log(color);
}) })
onMounted(() => { onMounted(() => {
@ -24,14 +21,14 @@ onMounted(() => {
'h1', 'h1',
{ {
opacity: 0, opacity: 0,
zoom: 0.95, zoom: 0.95
}, },
{ {
opacity: 1, opacity: 1,
duration: 1, duration: 1,
zoom: 1, zoom: 1,
ease: 'power2.out', ease: 'power2.out',
}, }
) )
ScrollTrigger.create({ ScrollTrigger.create({
@ -51,7 +48,7 @@ onMounted(() => {
// opacity: 1, // opacity: 1,
// duration: 1, // duration: 1,
ease: 'power2.out', ease: 'power2.out',
}, }
) )
ScrollTrigger.create({ ScrollTrigger.create({
@ -60,18 +57,22 @@ onMounted(() => {
onEnter: () => animh2.restart(), // play when scrolling down onEnter: () => animh2.restart(), // play when scrolling down
onEnterBack: () => animh2.restart(), // play again when scrolling up onEnterBack: () => animh2.restart(), // play again when scrolling up
}) })
}) })
const route = useRoute()
const store = useStore() const route = useRoute();
const menuStore = useMenuStore() const store = useStore();
await store.getSections(route.params.id) const menuStore = useMenuStore();
await store.getSections(route.params.id);
onMounted(() => { onMounted(() => {
menuStore.openMenu = false menuStore.openMenu = false;
}) });
useHead(menuStore.headMeta) useHead(menuStore.headMeta);
const componentsList = await store.getComponents(route.params.id) const componentsList = await store.getComponents(route.params.id)
</script> </script>

View File

@ -1,29 +1,26 @@
<template> <template>
<KeepAlive> <KeepAlive>
<component <component :is="component.componentInstance" v-for="component in componentsList" :key="component.name"
:is="component.componentInstance" :component="component.component" />
v-for="component in componentsList"
:key="component.name"
:component="component.component"
/>
</KeepAlive> </KeepAlive>
</template> </template>
<script setup> <script setup>
const menuStore = useMenuStore() const menuStore = useMenuStore();
const route = useRoute() const route = useRoute();
route.params.id = menuStore.defaultMenu.id route.params.id = menuStore.defaultMenu.id;
route.params.slug = menuStore.defaultMenu.link_rewrite route.params.slug = menuStore.defaultMenu.link_rewrite;
const store = useStore() const store = useStore();
await store.getSections(route.params.id) await store.getSections(route.params.id);
onMounted(() => { onMounted(() => {
menuStore.openMenu = false menuStore.openMenu = false;
}) });
useHead(menuStore.headMeta) useHead(menuStore.headMeta);
const componentsList = await store.getComponents(route.params.id) const componentsList = await store.getComponents(route.params.id);
</script> </script>

View File

@ -1,26 +1,30 @@
import type { VueI18n } from 'vue-i18n' import type { VueI18n } from "vue-i18n";
import type { RouteLocation, Router } from 'vue-router' import type { RouteLocation, Router } from "vue-router";
import type { CookieData, GenericResponse } from '~/types' import type { CookieData, GenericResponse } from "~/types";
// Extend the NuxtApp type // Extend the NuxtApp type
declare module '#app' { declare module '#app' {
interface NuxtApp { interface NuxtApp {
$session: Session $session: Session;
} }
} }
declare module 'vue' { declare module 'vue' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
$session: Session $session: Session;
} }
} }
export class Session { export class Session {
cookieData = ref({} as CookieData)
cookieData = ref({} as CookieData);
urlParams = new URLSearchParams() urlParams = new URLSearchParams()
currentLanguageIso = ref('' as string) currentLanguageIso = ref("" as string)
currentCountryIso = ref('' as string) currentCountryIso = ref("" as string)
currentCurrencyIso = ref('' as string) currentCurrencyIso = ref("" as string)
route = {} as RouteLocation route = {} as RouteLocation
router = {} as Router router = {} as Router
@ -33,43 +37,30 @@ export class Session {
this.router = router this.router = router
this.i18n = i18n this.i18n = i18n
this.setLanguage(
this.route.query?.lang_iso this.setLanguage(this.route.query?.lang_iso ? this.route.query?.lang_iso as string : unref(i18n.locale))
? (this.route.query?.lang_iso as string) this.setCountry(this.route.query?.country_iso ? this.route.query?.country_iso as string : "")
: unref(i18n.locale), this.setCurrency(this.route.query?.currency_iso ? this.route.query?.currency_iso as string : "")
)
this.setCountry(
this.route.query?.country_iso
? (this.route.query?.country_iso as string)
: '',
)
this.setCurrency(
this.route.query?.currency_iso
? (this.route.query?.currency_iso as string)
: '',
)
} }
async loadSession() { async loadSession() {
if (this.sessionOngoing) return if (this.sessionOngoing) return
this.sessionOngoing = true this.sessionOngoing = true
this.setQueryParams() this.setQueryParams()
const { data } = await useMyFetch<GenericResponse<CookieData>>( const { data } = await useMyFetch<GenericResponse<CookieData>>(`/api/public/cookie?${this.urlParams.toString()}`, {
`/api/public/cookie?${this.urlParams.toString()}`,
{
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, });
) this.cookieData.value = data;
this.cookieData.value = data
this.currentCountryIso.value = this.cookieData.value.country.iso_code this.currentCountryIso.value = this.cookieData.value.country.iso_code
this.currentLanguageIso.value = this.cookieData.value.language.iso_code this.currentLanguageIso.value = this.cookieData.value.language.iso_code
this.currentCurrencyIso.value = this.cookieData.value.currency.iso_code this.currentCurrencyIso.value = this.cookieData.value.currency.iso_code
setTimeout(() => (this.sessionOngoing = false), 2000) setTimeout(() => this.sessionOngoing = false, 2000)
} }
setLanguage(iso: string) { setLanguage(iso: string) {
@ -86,53 +77,56 @@ export class Session {
setQueryParams() { setQueryParams() {
if (this.currentLanguageIso.value.length > 0) { if (this.currentLanguageIso.value.length > 0) {
this.urlParams.set('lang_iso', this.currentLanguageIso.value) this.urlParams.set("lang_iso", this.currentLanguageIso.value)
} } else {
else { this.urlParams.delete("lang_iso")
this.urlParams.delete('lang_iso')
} }
if (this.currentCountryIso.value.length > 0) { if (this.currentCountryIso.value.length > 0) {
this.urlParams.set('country_iso', this.currentCountryIso.value) this.urlParams.set("country_iso", this.currentCountryIso.value)
} } else {
else { this.urlParams.delete("country_iso")
this.urlParams.delete('country_iso')
} }
if (this.currentCurrencyIso.value.length > 0) { if (this.currentCurrencyIso.value.length > 0) {
this.urlParams.set('currency_iso', this.currentCurrencyIso.value) this.urlParams.set("currency_iso", this.currentCurrencyIso.value)
} } else {
else { this.urlParams.delete("currency_iso")
this.urlParams.delete('currency_iso')
} }
} }
} }
export default defineNuxtPlugin(async (nuxtApp) => {
const loaded = [] as Array<string>
const { $i18n: i18n } = nuxtApp as unknown as { $i18n: VueI18n } export default defineNuxtPlugin(async (nuxtApp) => {
const { $router: router } = nuxtApp as unknown as { $router: Router } const loaded = [] as Array<string>;
const { $i18n: i18n } = nuxtApp as unknown as { $i18n: VueI18n };
const { $router: router } = nuxtApp as unknown as { $router: Router };
i18n.onBeforeLanguageSwitch = async (_, newLocale) => { i18n.onBeforeLanguageSwitch = async (_, newLocale) => {
if (loaded.includes(newLocale)) return if (loaded.includes(newLocale)) return;
try { try {
loaded.push(newLocale) loaded.push(newLocale);
const { data } = await useMyFetch<GenericResponse<object>>( const { data } = await useMyFetch<GenericResponse<object>>(
'/api/public/front/translation', "/api/public/front/translation"
) );
i18n.setLocaleMessage(newLocale, data) i18n.setLocaleMessage(newLocale, data);
}
catch (err) { } catch (err) {
console.error('❌ Failed to load translation for locale:', newLocale) console.error("❌ Failed to load translation for locale:", newLocale);
throw err throw err;
}
} }
};
const session = new Session(i18n, router) const session = new Session(i18n, router)
await session.loadSession() await session.loadSession();
nuxtApp.provide('session', session) nuxtApp.provide("session", session);
})
});

View File

@ -1,10 +1,10 @@
import { defineNuxtPlugin } from '#app' import { defineNuxtPlugin } from "#app";
export default defineNuxtPlugin(async () => { export default defineNuxtPlugin(async () => {
const menuStore = useMenuStore() const menuStore = useMenuStore();
await menuStore.loadMenu() await menuStore.loadMenu();
await menuStore.getLocales() await menuStore.getLocales();
const store = useStore() const store = useStore();
await store.getMinValue() await store.getMinValue();
await store.getCalculator() await store.getCalculator();
}) });

View File

@ -1,10 +1,10 @@
import * as Vue3Toastify from 'vue3-toastify' import * as Vue3Toastify from "vue3-toastify";
import 'vue3-toastify/dist/index.css' import "vue3-toastify/dist/index.css";
export default defineNuxtPlugin((nuxtApp) => { export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(Vue3Toastify.default, { autoClose: 2000 }) nuxtApp.vueApp.use(Vue3Toastify.default, { autoClose: 2000 });
return { return {
provide: { toast: Vue3Toastify.toast }, provide: { toast: Vue3Toastify.toast },
} };
}) });

11878
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,3 +1,3 @@
<svg width="76" height="53" viewBox="0 0 76 53" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="76" height="53" viewBox="0 0 76 53" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M76 49.8V2C76 0.9 75.1 0 74 0H2C0.9 0 0 0.9 0 2V50C0 52.5 3.3 52 2 52H74C77 52 75.6 47.4 76 49.8ZM6 48L28 19.3L46.4 43.4L49.9 48H6ZM55 48L50.5 42.2L58 32.4L69.9 48H55ZM72 44L59.6 27.8C58.8 26.7 57.2 26.7 56.4 27.8L48 38.8L29.6 14.8C28.8 13.7 27.2 13.7 26.4 14.8L4 44.1V4H72V44ZM49 10C45.1 10 42 13.1 42 17C42 20.9 45.1 24 49 24C52.9 24 56 20.9 56 17C56 13.1 52.8 10 49 10ZM49 20C47.4 20 46 18.7 46 17C46 15.3 47.3 14 49 14C50.7 14 52 15.3 52 17C52 18.7 50.6 20 49 20Z" fill="#525252"/> <path d="M76 49.8V2C76 0.9 75.1 0 74 0H2C0.9 0 0 0.9 0 2V50C0 52.5 3.3 52 2 52H74C77 52 75.6 47.4 76 49.8ZM6 48L28 19.3L46.4 43.4L49.9 48H6ZM55 48L50.5 42.2L58 32.4L69.9 48H55ZM72 44L59.6 27.8C58.8 26.7 57.2 26.7 56.4 27.8L48 38.8L29.6 14.8C28.8 13.7 27.2 13.7 26.4 14.8L4 44.1V4H72V44ZM49 10C45.1 10 42 13.1 42 17C42 20.9 45.1 24 49 24C52.9 24 56 20.9 56 17C56 13.1 52.8 10 49 10ZM49 20C47.4 20 46 18.7 46 17C46 15.3 47.3 14 49 14C50.7 14 52 15.3 52 17C52 18.7 50.6 20 49 20Z" fill="#004F3D"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

View File

@ -1,110 +1,94 @@
import { validation } from '../utils/validation' import type { GenericResponse } from "~/types";
import { REGEX_PHONE } from '../utils/regex' import type { AddressesList, UserAddressOfficial } from "~/types/checkout";
import type { GenericResponse, GenericResponseItems, UserCart } from '~/types' import { validation } from "../utils/validation";
import type { import { REGEX_PHONE } from "../utils/regex";
AddressesList,
CheckoutAddress,
CheckoutOrder,
Payment,
UserAddressOfficial,
} from '~/types/checkout'
import type { CartProduct } from '~/types/product'
export const useCheckoutStore = defineStore('checkoutStore', () => { export const useCheckoutStore = defineStore("checkoutStore", () => {
const { $toast } = useNuxtApp() const { $toast } = useNuxtApp();
const menuStore = useMenuStore() const menuStore = useMenuStore();
const selectedIso = ref(menuStore.selectedCountry);
const selectedIso = ref(menuStore.selectedCountry)
const vLegal = ref(false)
const vTerms = ref(false)
const vNote = ref('')
const legalValidation = ref(false)
const termsValidation = ref(false)
// get address list // get address list
const addressesList = ref<AddressesList[]>() const addressesList = ref<AddressesList[]>();
const activeAddress = ref<AddressesList | null>() const activeAddress = ref<AddressesList | null>();
async function getAddressList() { async function getAddressList() {
try { try {
const { data } = await useMyFetch<GenericResponse<AddressesList[]>>( const { data } = await useMyFetch<GenericResponse<AddressesList[]>>(
`/api/restricted/user/addresses`, `/api/restricted/user/addresses`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: async (_, status) => { onErrorOccured: async (_, status) => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
},
)
addressesList.value = data
activeAddress.value = addressesList.value[0]
} }
catch (error) { );
console.error('restrictedAddress error:', error)
addressesList.value = data;
activeAddress.value = addressesList.value[0];
} catch (error) {
console.error("restrictedAddress error:", error);
} }
} }
// get user data // get user data
const userName = ref('') const userName = ref("");
const lastName = ref('') const lastName = ref("");
const address = ref('') const address = ref("");
const postCode = ref('') const postCode = ref("");
const city = ref('') const city = ref("");
const country = ref('') const country = ref("");
const phoneNumber = ref('') const phoneNumber = ref("");
const accountPhoneNumber = ref('') const accountPhoneNumber = ref("");
async function getUserData() { async function getUserData() {
try { try {
const { data } = await useMyFetch<GenericResponse<UserAddressOfficial>>( const { data } = await useMyFetch<GenericResponse<UserAddressOfficial>>(
`/api/restricted/user/address/official`, `/api/restricted/user/address/official`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: async (_, status) => { onErrorOccured: async (_, status) => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
},
)
userName.value = data.address.name
lastName.value = data.address.surname
address.value = data.address.street
postCode.value = data.address.postcode
city.value = data.address.city
country.value = data.address.country.country_lang[0].name
} }
catch (error) { );
console.error('getUserData error:', error)
userName.value = data.address.name;
lastName.value = data.address.surname;
address.value = data.address.street;
postCode.value = data.address.postcode;
city.value = data.address.city;
country.value = data.address.country.country_lang[0].name;
} catch (error) {
console.error("getUserData error:", error);
} }
} }
// upload new address // upload new address
const vNewAddressName = ref('') const vNewAddressName = ref("");
const vNewAddressSurname = ref('') const vNewAddressSurname = ref("");
const vNewAddressAddress = ref('') const vNewAddressAddress = ref("");
const vNewAddressCode = ref('') const vNewAddressCode = ref("");
const vNewAddressCity = ref('') const vNewAddressCity = ref("");
const vNewAddressCountry = ref() const vNewAddressCountry = ref();
const vUseAccountPhoneNumber = ref(false) const vUseAccountPhoneNumber = ref(false);
const isOpen = ref<boolean>(false) const isOpen = ref<boolean>(false);
async function uploadAddress() { async function uploadAddress() {
try { try {
const res = await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/restricted/user/address`, `/api/restricted/user/address`,
{ {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
address: { address: {
@ -120,61 +104,59 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
}, }
) );
if (res.status === 200) { if (res.status === 200) {
$toast.success('Address successfully added', { $toast.success("Address successfully added", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
isOpen.value = false isOpen.value = false;
getAddressList() getAddressList();
} } else {
else { $toast.error("Failed to add address. Please try again.", {
$toast.error('Failed to add address. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (error) {
catch (error) { console.error("uploadAddress error:", error);
console.error('uploadAddress error:', error)
} }
} }
const currentPrefix = ref<string | number>( const currentPrefix = ref<string | number>(
menuStore.selectedCountry.call_prefix, menuStore.selectedCountry.call_prefix
) );
const changePrefix = (item: string) => { const changePrefix = (item: string) => {
currentPrefix.value = item currentPrefix.value = item;
} };
const phoneValidation = ref<boolean | null>(null) const phoneValidation = ref<boolean | null>(null);
// send checkout form const userStore = useUserStore();
const userStore = useUserStore() // send form
async function sendForm() { async function sendForm() {
const phoneNum = vUseAccountPhoneNumber.value let phoneNum = vUseAccountPhoneNumber.value
? accountPhoneNumber.value ? accountPhoneNumber.value
: `${currentPrefix.value}${phoneNumber.value}`.replaceAll(' ', '').trim() : `${currentPrefix.value}${phoneNumber.value}`.replaceAll(" ", "").trim();
// if (vUseAccountPhoneNumber.value) { // if (vUseAccountPhoneNumber.value) {
// phoneNum = phoneNumber.value; // phoneNum = phoneNumber.value;
// } // }
phoneValidation.value = validation(phoneNum, 1, 49, REGEX_PHONE) phoneValidation.value = validation(phoneNum, 1, 49, REGEX_PHONE);
if (!phoneValidation.value && !vUseAccountPhoneNumber.value) { if (!phoneValidation.value && !vUseAccountPhoneNumber.value) {
return return;
} }
try { try {
const res = await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/restricted/cart/checkout/delivery`, `/api/restricted/cart/checkout/delivery`,
{ {
method: 'PUT', method: "PUT",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
address: { address: {
@ -192,330 +174,54 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
}, }
) );
console.log(res)
if (res.status === 200) { if (res.status === 200) {
$toast.success('Form successfully sent', { $toast.success("Form successfully sent", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
menuStore.navigateToItem( // redirectToSummary();
menuStore.menuItems?.find(item => item.id === 13), } else {
) $toast.error("Failed to send form. Please try again.", {
}
else {
$toast.error('Failed to send form. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (error) {
catch (error) { console.error("uploadAddress error:", error);
console.error('uploadAddress error:', error)
} }
} }
// get checkout const changeActive = (item: any) => {
activeAddress.value = item;
};
async function getCheckout() { async function getCheckout() {
try { try {
await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/restricted/cart/checkout`, `/api/restricted/cart/checkout`,
{ {
method: 'PUT', method: "PUT",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: async (_, status) => { onErrorOccured: async (_, status) => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
},
)
} }
catch (error) { );
console.error('uploadAddress error:', error) } catch (error) {
console.error("uploadAddress error:", error);
} }
} }
// get user cart
const products = ref<CartProduct[]>()
const fullPrice = ref()
const fullProductsPrice = ref()
async function getUserCart() {
try {
const { data } = await useMyFetch<GenericResponse<UserCart>>(
`/api/public/user/cart`,
{
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
products.value = data.cart_items
fullPrice.value = data.total_value
fullProductsPrice.value = data.total_value
fullPrice.value = Number(fullPrice.value) + Number(shippingPrice.value)
}
catch (error) {
console.error('getUserCart error:', error)
}
}
// get delivery options
interface DeliveryOptionItem {
country_iso: string
country_name: string
delivery_supplier_id: number
delivery_supplier_name: string
id: number
shippment_price: string
}
const deliveryOption = ref<DeliveryOptionItem[]>([])
const currentDelivery = ref<DeliveryOptionItem | null>(null)
const shippingPrice = ref<number>(0)
async function getDeliveryOptions() {
try {
const { data } = await useMyFetch<GenericResponseItems<DeliveryOptionItem[]>>(
`/api/restricted/cart/checkout/delivery-options`,
{
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
if (data.items && data.items.length > 0) {
deliveryOption.value = data.items
currentDelivery.value = data.items[0]
shippingPrice.value = Number(data.items[0].shippment_price)
fullPrice.value += shippingPrice.value
}
}
catch (error) {
console.error('getDeliveryOptions error:', error)
}
}
const setCurrentDelivery = (item: DeliveryOptionItem) => {
shippingPrice.value = Number(item.shippment_price)
currentDelivery.value = item
fullPrice.value = Number(fullPrice.value) + Number(shippingPrice.value)
}
const defaultAddress = ref<CheckoutAddress | undefined>()
async function getDefAddress() {
try {
const { data } = await useMyFetch<GenericResponse<{ addresses: Address[] }>>(
`/api/public/user`,
{
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
defaultAddress.value = data.addresses.find(
el => el.is_default === true || el.is_default === 'true',
)
}
catch (error) {
console.error('getUserCart error:', error)
}
}
// get bank data
const paymentMethods = ref([] as Payment[])
const fullAddress = ref<Address>()
const currentPayment = ref<Payment | null>()
async function getBankAccount() {
try {
const { data } = await useMyFetch<GenericResponse<Payment[]>>(
`/api/restricted/suitable-bank-accounts/${menuStore.selectedCurrency.iso_code}/${fullAddress.value?.country_iso}`,
{
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
paymentMethods.value = data
currentPayment.value = data[0]
}
catch (error) {
console.error('getUserCart error:', error)
}
}
// get order (summary)
async function getOrder() {
try {
const { data } = await useMyFetch<GenericResponse<CheckoutOrder>>(
`/api/restricted/cart/checkout/order`,
{
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
fullAddress.value = data.delivery_details.address
}
catch (error) {
console.error('getOrder error:', error)
}
}
async function setNewAddress(indexItem: number) {
currentPayment.value = paymentMethods.value.find(
(item, index) => indexItem === index,
)
}
// send summary form
async function sendSummaryForm() {
legalValidation.value = !vLegal.value
termsValidation.value = !vTerms.value
if (!vTerms.value && !vLegal.value) {
return
}
try {
await useMyFetch<GenericResponse<object>>(
`/api/restricted/cart/checkout/delivery`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
accept_general_conditions: true,
accept_long_purchase: true,
address: fullAddress.value,
delivery_option_id: currentDelivery.value?.id,
note: vNote.value,
}),
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
await putCheckoutBankAccount()
await markOrder()
await getUserCart()
}
catch (error) {
console.error('uploadAddress error:', error)
}
}
// put checkout bank-account
async function putCheckoutBankAccount() {
try {
await useMyFetch<GenericResponse<object>>(
`/api/restricted/cart/checkout/bank-account/${currentPayment.value?.id}`,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
}
catch (error) {
console.error('uploadAddress error:', error)
}
}
const modalMadeOrder = ref(false)
async function markOrder() {
try {
const res = await useMyFetch<GenericResponse<object>>(
`/api/restricted/cart/checkout/order`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
onErrorOccured: async (_, status) => {
throw createError({
statusCode: status,
statusMessage: `HTTP error: ${status}`,
})
},
},
)
if (res.status === 200 || res.status === 201) {
$toast.success('Address successfully added', {
autoClose: 5000,
dangerouslyHTMLString: true,
})
modalMadeOrder.value = true
}
else {
$toast.error('Failed to add address. Please try again.', {
autoClose: 5000,
dangerouslyHTMLString: true,
})
}
// window.location.href = `/golden-panel/my-purchases/${res._data?.data.id}`;
}
catch (error) {
console.error('uploadAddress error:', error)
}
}
return { return {
addressesList, addressesList,
activeAddress, activeAddress,
@ -541,37 +247,12 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
vNewAddressCity, vNewAddressCity,
vNewAddressCountry, vNewAddressCountry,
products,
fullPrice,
fullProductsPrice,
deliveryOption,
currentDelivery,
shippingPrice,
defaultAddress,
paymentMethods,
currentPayment,
fullAddress,
vLegal,
vTerms,
vNote,
legalValidation,
termsValidation,
modalMadeOrder,
changePrefix, changePrefix,
getCheckout, getCheckout,
getAddressList, getAddressList,
getUserData, getUserData,
changeActive,
uploadAddress, uploadAddress,
sendForm, sendForm,
getUserCart, };
getDeliveryOptions, });
getDefAddress,
setCurrentDelivery,
getBankAccount,
getOrder,
setNewAddress,
sendSummaryForm,
}
})

View File

@ -1,4 +1,3 @@
import { useStore } from './store'
import type { import type {
Country, Country,
Currency, Currency,
@ -7,194 +6,146 @@ import type {
GenericResponseItems, GenericResponseItems,
Language, Language,
UIFrontMenu, UIFrontMenu,
} from '~/types' } from "~/types";
import { useMyFetch } from '#imports' import { useStore } from "./store";
import { useMyFetch } from "#imports";
// import { useSession } from "~/plugins/01_i18n";
function buildTreeRecursive( function buildTreeRecursive(
data: (FrontMenu | UIFrontMenu)[], data: (FrontMenu | UIFrontMenu)[],
parentId: number, parentId: number
): UIFrontMenu[] { ): UIFrontMenu[] {
const children = data.filter( const children = data.filter(
(item): item is UIFrontMenu => (item): item is UIFrontMenu =>
item.id_parent === parentId && !item.is_default, item.id_parent === parentId && !item.is_default
) );
return children.map(item => ({ return children.map((item) => ({
...item, ...item,
children: buildTreeRecursive(data, item.id), children: buildTreeRecursive(data, item.id),
})) }));
} }
export const useMenuStore = defineStore('menuStore', () => { export const useMenuStore = defineStore("menuStore", () => {
const store = useStore() const store = useStore();
const { $i18n } = useNuxtApp() const { $i18n } = useNuxtApp();
// const session = useSession(); // const session = useSession();
const { $session } = useNuxtApp() const { $session } = useNuxtApp();
const router = useRouter() const router = useRouter();
const route = useRoute() const route = useRoute();
const openMenu = ref(false) const openMenu = ref(false);
const openDropDown = ref(false) const openDropDown = ref(false);
const defaultMenu = ref() const defaultMenu = ref();
const menu = ref([] as UIFrontMenu[]);
const menuItems = ref([] as FrontMenu[]);
const menu = ref([] as UIFrontMenu[])
const menuItems = ref([] as FrontMenu[])
// curr/country // curr/country
const selectedCountry = ref({} as Country) const selectedCountry = ref({} as Country);
const selectedPhoneCountry = ref({} as Country) const selectedPhoneCountry = ref({} as Country);
const selectedCurrency = ref({} as Currency) const selectedCurrency = ref({} as Currency);
const selectedLanguage = ref({} as Language) const selectedLanguage = ref({} as Language);
const countries = ref([] as Country[])
const currencies = ref([] as Currency[]) const countries = ref([] as Country[]);
const languages = ref([] as Language[]) const currencies = ref([] as Currency[]);
const languages = ref([] as Language[]);
const getLocales = async () => { const getLocales = async () => {
const { data: countriesList } = await useMyFetch< const { data: countriesList } = await useMyFetch<GenericResponse<Country[]>>(`/api/public/country/list`);
GenericResponse<Country[]> countries.value = countriesList;
>(`/api/public/country/list`) selectedCountry.value = countriesList.find((country) => country.iso_code === $session.currentCountryIso.value) as Country;
countries.value = countriesList selectedPhoneCountry.value = countriesList.find((country) => country.iso_code === $session.currentCountryIso.value) as Country;
selectedCountry.value = countriesList.find(
country => country.iso_code === $session.currentCountryIso.value,
) as Country
selectedPhoneCountry.value = countriesList.find(
country => country.iso_code === $session.currentCountryIso.value,
) as Country
const { data: currenciesList } = await useMyFetch<
GenericResponseItems<Currency[]>
>(`/api/public/currencies`)
currencies.value = currenciesList.items
selectedCurrency.value = currenciesList.items.find(
currency => currency.iso_code === $session.currentCurrencyIso.value,
) as Currency
const { data: languagesList } = await useMyFetch< const { data: currenciesList } = await useMyFetch<GenericResponseItems<Currency[]>>(`/api/public/currencies`)
GenericResponseItems<Language[]> currencies.value = currenciesList.items;
>(`/api/public/languages`) selectedCurrency.value = currenciesList.items.find((currency) => currency.iso_code === $session.currentCurrencyIso.value) as Currency;
languages.value = languagesList.items
selectedLanguage.value = languagesList.items.find( const { data: languagesList } = await useMyFetch<GenericResponseItems<Language[]>>(`/api/public/languages`)
language => language.iso_code === $session.currentLanguageIso.value, languages.value = languagesList.items;
) as Language selectedLanguage.value = languagesList.items.find((language) => language.iso_code === $session.currentLanguageIso.value) as Language;
} };
const loadMenu = async () => { const loadMenu = async () => {
try { try {
const { data } = await useMyFetch<GenericResponse<FrontMenu[]>>( const { data } = await useMyFetch<GenericResponse<FrontMenu[]>>(
`/api/public/front/menu`, `/api/public/front/menu`,
{ {
onErrorOccured: (err, status) => { onErrorOccured: (err, status) => {
console.log(err, status) console.log(err, status);
}, },
}, }
) );
menuItems.value = data menuItems.value = data;
const root = data.find(item => item.is_root) as UIFrontMenu const root = data.find((item) => item.is_root) as UIFrontMenu;
defaultMenu.value = data.find(item => item.is_default) defaultMenu.value = data.find((item) => item.is_default);
if (root) { if (root) {
menu.value = buildTreeRecursive(data, root.id) menu.value = buildTreeRecursive(data, root.id);
} } else {
else { console.warn("Root menu item not found");
console.warn('Root menu item not found') menu.value = [];
menu.value = []
}
}
catch (error) {
console.log(error)
} }
} catch (error) {
console.log(error);
} }
};
const navigateToItem = (item?: UIFrontMenu) => { const navigateToItem = (item?: UIFrontMenu) => {
if (item) { if (item) {
router.push({ router.push({
params: { slug: item.front_menu_lang[0].link_rewrite, id: item.id }, params: { slug: item.front_menu_lang[0].link_rewrite, id: item.id },
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.front_menu_lang[0].link_rewrite, slug: defaultMenu.value.front_menu_lang[0].link_rewrite,
id: defaultMenu.value.id, id: defaultMenu.value.id,
}, },
name: `id-slug___${$i18n.locale.value}`, name: `id-slug___${$i18n.locale.value}`,
}) });
}
} }
};
function navigateToShop() { function navigateToShop() {
navigateToItem(menuItems.value?.find(item => item.id === 5)) navigateToItem(menuItems.value?.find((item) => item.id === 5));
} }
function getShopMenu() { const getFirstImage = (size: "l" | "m" | "s" = "m", needbaseurl: boolean) => {
return menuItems.value?.find(item => item.id === 5) const req = useRequestEvent();
} const url = useRequestURL();
function getProductMenu() {
return menuItems.value?.find(item => item.id === 14)
}
function getRegistrationMenu() {
return menuItems.value?.find(item => item.id === 4)
}
function getLoginMenu() {
return menuItems.value?.find(item => item.id === 11)
}
function getInvestitionMenu() {
return menuItems.value?.find(item => item.id === 6)
}
function getAboutUsMenu() {
return menuItems.value?.find(item => item.id === 7)
}
function getBusinessMenu() {
return menuItems.value?.find(item => item.id === 8)
}
function getAboutGoldMenu() {
return menuItems.value?.find(item => item.id === 9)
}
function getContactMenu() {
return menuItems.value?.find(item => item.id === 10)
}
const getFirstImage = (size: 'l' | 'm' | 's' = 'm', needbaseurl: boolean) => {
const req = useRequestEvent()
const url = useRequestURL()
// let img = ""; // let img = "";
const img: string[] = [] const img: string[] = []
for (const s in store.components) { for (const s in store.components) {
if (store.components[s].front_section.img.length === 0) continue if (store.components[s].front_section.img.length === 0) continue;
img.push( img.push(`/api/public/file/${store.components[s].front_section.img[0]}_${size}.webp`)
`/api/public/file/${store.components[s].front_section.img[0]}_${size}.webp`, if (img.length > 0) break;;
)
if (img.length > 0) break
} }
if (img.length > 0) { if (img.length > 0) {
if (needbaseurl) { if (needbaseurl) {
return `${req?.headers.get('x-forwarded-proto') || url.protocol}://${req?.headers.get('x-forwarded-host') return `${req?.headers.get("x-forwarded-proto") || url.protocol}://${req?.headers.get("x-forwarded-host") || url.host || req?.headers.get("host")}${img[0]}`;
|| url.host
|| req?.headers.get('host')
}${img[0]}`
} }
return img[0] return img[0];
}
return ''
} }
return "";
};
const headMeta = computed(() => { const headMeta = computed(() => {
const item = menuItems.value?.find( const item = menuItems.value?.find(
item => item.id.toString() === route.params.id, (item) => item.id.toString() === route.params.id
) );
const meta = { const meta = {
title: item?.front_menu_lang[0].meta_title, title: item?.front_menu_lang[0].meta_title,
@ -203,77 +154,56 @@ export const useMenuStore = defineStore('menuStore', () => {
}, },
link: [ link: [
// { rel: "manifest", href: "/api/manifest.json" } // { rel: "manifest", href: "/api/manifest.json" }
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.png' },
],
script: [
{
src: 'https://leiadmin.com/leitag.js?lei=894500UT83EISNNA8D04&color=dark',
defer: true,
},
], ],
meta: [ meta: [
{ {
hid: 'description', hid: "description",
name: 'description', name: "description",
content: item?.front_menu_lang[0].meta_description, content: item?.front_menu_lang[0].meta_description,
}, },
{ {
property: 'og:title', property: "og:title",
content: item?.front_menu_lang[0].meta_title, content: item?.front_menu_lang[0].meta_title,
}, },
{ {
property: 'og:description', property: "og:description",
content: item?.front_menu_lang[0].meta_description, content: item?.front_menu_lang[0].meta_description,
}, },
{ {
property: 'og:image', property: "og:image",
content: getFirstImage('m', true), content: getFirstImage("m", true),
}, },
{ {
property: 'twitter:title', property: "twitter:title",
content: item?.front_menu_lang[0].meta_title, content: item?.front_menu_lang[0].meta_title,
}, },
{ {
property: 'twitter:description', property: "twitter:description",
content: item?.front_menu_lang[0].meta_description, content: item?.front_menu_lang[0].meta_description,
}, },
{ {
property: 'twitter:image', property: "twitter:image",
content: getFirstImage('m', true), content: getFirstImage("m", true),
}, },
], ],
} };
const preload = getFirstImage('l', false) const preload = getFirstImage("l", false);
if (preload) { if (preload) {
meta.link.push({ rel: 'preload', as: 'image', href: preload } as never) meta.link.push({ rel: "preload", as: "image", href: preload } as never);
} }
return meta return meta;
})
const formatPrice = (value: number): string => { });
return value.toLocaleString(selectedLanguage.value.iso_code, {
minimumFractionDigits: selectedCurrency.value.precision,
maximumFractionDigits: selectedCurrency.value.precision,
currency: selectedCurrency.value.iso_code,
style: 'currency',
currencyDisplay: 'symbol',
currencySign: 'accounting',
})
}
// watches // watches
watch( watch(() => $session.cookieData, async () => {
() => $session.cookieData, await getLocales();
async () => { await loadMenu();
await getLocales() await store.getMinValue();
await loadMenu() await store.getCalculator();
await store.getMinValue() }, { deep: true });
await store.getCalculator()
},
{ deep: true },
)
return { return {
menu, menu,
@ -289,20 +219,9 @@ export const useMenuStore = defineStore('menuStore', () => {
selectedLanguage, selectedLanguage,
defaultMenu, defaultMenu,
headMeta, headMeta,
navigateToShop, navigateToShop,
loadMenu, loadMenu,
navigateToItem, navigateToItem,
getLocales, getLocales,
getProductMenu, };
formatPrice, });
getRegistrationMenu,
getLoginMenu,
getInvestitionMenu,
getAboutUsMenu,
getBusinessMenu,
getAboutGoldMenu,
getContactMenu,
getShopMenu,
}
})

View File

@ -1,17 +1,17 @@
import { useMyFetch } from '#imports' import { useMyFetch } from "#imports";
import type { import type {
CartItem,
GenericResponse, GenericResponse,
GenericResponseChildren, GenericResponseChildren,
GenericResponseItems, GenericResponseItems,
} from '~/types' UserCart,
import type { Product } from '~/types/product' } from "~/types";
import type { Product } from "~/types/product";
export const useProductStore = defineStore('productStore', () => { export const useProductStore = defineStore("productStore", () => {
const { $toast } = useNuxtApp() const { $toast } = useNuxtApp();
const productList = ref<Product[]>() const productList = ref<Product[]>();
const modules = ref() const modules = ref();
const checkoutStore = useCheckoutStore()
async function getList(count: number, categoryId = 1) { async function getList(count: number, categoryId = 1) {
try { try {
@ -19,22 +19,21 @@ export const useProductStore = defineStore('productStore', () => {
`/api/public/products/category/${categoryId}?p=1&elems=${count}`, `/api/public/products/category/${categoryId}?p=1&elems=${count}`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: async (_, status) => { onErrorOccured: async (_, status) => {
// await navigateTo("/error", { replace: true }); // await navigateTo("/error", { replace: true });
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
},
)
productList.value = data.items
} }
catch (error) { );
console.error('getList error:', error)
productList.value = data.items;
} catch (error) {
console.error("getList error:", error);
} }
} }
@ -44,21 +43,20 @@ export const useProductStore = defineStore('productStore', () => {
`/api/public/module/e_shop`, `/api/public/module/e_shop`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
modules.value = data.children.find( modules.value = data.children.find(
(item: { id: number, name: string }) => (item: { id: number; name: string }) =>
item.name === 'currency_rates_bar', item.name === "currency_rates_bar"
) );
} } catch (error) {
catch (error) { console.error("getList error:", error);
console.error('getList error:', error)
} }
} }
@ -67,36 +65,34 @@ export const useProductStore = defineStore('productStore', () => {
const res = await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/public/user/cart/item/add/${id}/1`, `/api/public/user/cart/item/add/${id}/1`,
{ {
method: 'PUT', method: "PUT",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
if (res.status === 200) { if (res.status === 200) {
$toast.success('Item successfully added to your cart.', { $toast.success("Item successfully added to your cart.", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
await checkoutStore.getUserCart() getCart();
} } else {
else { $toast.error("Failed to add item to cart. Please try again.", {
$toast.error('Failed to add item to cart. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (error) {
catch (error) { $toast.error("An unexpected error occurred while updating your cart.", {
$toast.error('An unexpected error occurred while updating your cart.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
console.error('incrementCartItem error:', error) console.error("incrementCartItem error:", error);
} }
} }
@ -105,36 +101,34 @@ export const useProductStore = defineStore('productStore', () => {
const res = await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/public/user/cart/item/subtract/${id}/1`, `/api/public/user/cart/item/subtract/${id}/1`,
{ {
method: 'PUT', method: "PUT",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
if (res.status === 200) { if (res.status === 200) {
$toast.success('Item successfully removed from your cart.', { $toast.success("Item successfully removed from your cart.", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
await checkoutStore.getUserCart() getCart();
} } else {
else { $toast.error("Failed to removed item from cart. Please try again.", {
$toast.error('Failed to removed item from cart. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (error) {
catch (error) { $toast.error("An unexpected error occurred while updating your cart.", {
$toast.error('An unexpected error occurred while updating your cart.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
console.error('decrementCartItem error:', error) console.error("decrementCartItem error:", error);
} }
} }
@ -143,46 +137,67 @@ export const useProductStore = defineStore('productStore', () => {
const res = await useMyFetch<GenericResponse<object>>( const res = await useMyFetch<GenericResponse<object>>(
`/api/public/user/cart/item/${id}`, `/api/public/user/cart/item/${id}`,
{ {
method: 'DELETE', method: "DELETE",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
if (res.status === 200) { if (res.status === 200) {
$toast.success('Item successfully removed from your cart.', { $toast.success("Item successfully removed from your cart.", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
await checkoutStore.getUserCart() getCart();
} } else {
else { $toast.error("Failed to removed item from cart. Please try again.", {
$toast.error('Failed to removed item from cart. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (error) {
catch (error) { $toast.error("An unexpected error occurred while updating your cart.", {
$toast.error('An unexpected error occurred while updating your cart.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
console.error('deleteCartItem error:', error) console.error("deleteCartItem error:", error);
}
}
const cart = ref({} as UserCart);
async function getCart() {
try {
const { data } = await useMyFetch<GenericResponse<UserCart>>(
`/api/public/user/cart`,
{
headers: {
"Content-Type": "application/json",
},
onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`);
},
}
);
cart.value = data;
} catch (error) {
console.error("getList error:", error);
} }
} }
return { return {
productList, productList,
modules, modules,
cart,
getList, getList,
getModules, getModules,
incrementCartItem, incrementCartItem,
decrementCartItem, decrementCartItem,
deleteCartItem, deleteCartItem,
} getCart,
}) };
});

View File

@ -1,66 +1,65 @@
import { useMyFetch } from '#imports' import { useMyFetch } from "#imports";
import type { import type {
componentsListType, componentsListType,
GenericResponse, GenericResponse,
PlanPrediction, PlanPrediction,
} from '~/types' } from "~/types";
import type { FrontPageSection } from '~/types/frontSection' import type { FrontPageSection } from "~/types/frontSection";
export const useStore = defineStore('store', () => { export const useStore = defineStore("store", () => {
const currentPageID = ref('') const currentPageID = ref("");
const { $toast } = useNuxtApp();
// calculator // calculator
const monthlySavings = ref(137) const monthlySavings = ref(137);
const storagePeriod = ref(10) const storagePeriod = ref(10);
const totalInvestment: Ref<number> = ref(0) const totalInvestment: Ref<number> = ref(0);
const minValue = ref() const minValue = ref();
const components = ref({} as FrontPageSection[]) const components = ref({} as FrontPageSection[]);
const getSections = async (id: string) => { const getSections = async (id: string) => {
const { data } = await useMyFetch<GenericResponse<FrontPageSection[]>>( const { data } = await useMyFetch<GenericResponse<FrontPageSection[]>>(
`/api/public/front/sections/${id}`, `/api/public/front/sections/${id}`
) );
components.value = data components.value = data;
} };
async function getComponents(): Promise<componentsListType[]> { async function getComponents(): Promise<componentsListType[]> {
try { try {
const children = components.value const children = components.value;
if (!children || !Array.isArray(children)) { if (!children || !Array.isArray(children)) {
console.warn('No components available in store.') console.warn("No components available in store.");
return [] return [];
} }
const componentsList = [] as componentsListType[] const componentsList = [] as componentsListType[];
for (const child of children) { for (const child of children) {
const componentName = child.front_section.component_name const componentName = child.front_section.component_name;
if (!componentName) continue if (!componentName) continue;
try { try {
const componentInstance = ( const componentInstance = (
await import(`@/components/section/${componentName}.vue`) await import(`@/components/section/${componentName}.vue`)
).default ).default;
const nonReactiveComponent = markRaw(componentInstance) const nonReactiveComponent = markRaw(componentInstance);
componentsList.push({ componentsList.push({
name: componentName, name: componentName,
component: child.front_section, component: child.front_section,
componentInstance: nonReactiveComponent, componentInstance: nonReactiveComponent,
}) });
} } catch (error) {
catch (error) { console.error(`Failed to load component ${componentName}`, error);
console.error(`Failed to load component ${componentName}`, error)
} }
} }
return componentsList return componentsList;
} catch (error) {
console.error("Failed to process components list", error);
} }
catch (error) { return [];
console.error('Failed to process components list', error)
}
return []
} }
async function getCalculator() { async function getCalculator() {
@ -69,39 +68,37 @@ export const useStore = defineStore('store', () => {
`/api/public/plan-prediction/easy/calculate?monthly_deposit=${monthlySavings.value}&years=${storagePeriod.value}`, `/api/public/plan-prediction/easy/calculate?monthly_deposit=${monthlySavings.value}&years=${storagePeriod.value}`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
},
)
totalInvestment.value = data.total_investement_value
} }
catch (error) { );
console.error('getList error:', error)
totalInvestment.value = data.total_investement_value;
} catch (error) {
console.error("getList error:", error);
} }
} }
async function getMinValue() { async function getMinValue() {
try { try {
const { data } = await useMyFetch<GenericResponse<number>>( const { data } = await useMyFetch<GenericResponse<number>>(
'/api/public/plan-prediction/free/minimum', "/api/public/plan-prediction/free/minimum",
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
},
)
minValue.value = data
} }
catch (error) { );
console.error('getList error:', error)
minValue.value = data;
} catch (error) {
console.error("getList error:", error);
} }
} }
@ -116,5 +113,5 @@ export const useStore = defineStore('store', () => {
getComponents, getComponents,
getSections, getSections,
getMinValue, getMinValue,
} };
}) });

View File

@ -1,16 +1,16 @@
import type { GenericResponse } from '~/types' import type { GenericResponse } from "~/types";
import type { Customer } from '~/types/user' import type { Customer } from "~/types/user";
export const useUserStore = defineStore('userStore', () => { export const useUserStore = defineStore("userStore", () => {
const store = useStore() const store = useStore();
const menuStore = useMenuStore() const menuStore = useMenuStore();
const checkoutStore = useCheckoutStore() const checkoutStore = useCheckoutStore();
const { $toast } = useNuxtApp() const { $toast } = useNuxtApp();
const fullUserData = ref<Customer | null>(null) const fullUserData = ref<Customer | null>(null);
const isLogged = ref<boolean>(true) const isLogged = ref<boolean>(true);
const user = ref<string | null>(null) const user = ref<string | null>(null);
async function checkIsLogged() { async function checkIsLogged() {
try { try {
@ -18,128 +18,120 @@ export const useUserStore = defineStore('userStore', () => {
`/api/public/user`, `/api/public/user`,
{ {
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: async (_, status) => { onErrorOccured: async (_, status) => {
throw createError({ throw createError({
statusCode: status, statusCode: status,
statusMessage: `HTTP error: ${status}`, statusMessage: `HTTP error: ${status}`,
}) });
}, },
}, }
) );
if ('loggedin' in data && data.loggedin === true) { if ("loggedin" in data && data.loggedin === true) {
isLogged.value = true isLogged.value = true;
user.value = `${data.first_name} ${data.last_name}` as string user.value = `${data.first_name} ${data.last_name}` as any;
fullUserData.value = data fullUserData.value = data;
checkoutStore.accountPhoneNumber = fullUserData.value.phone_number checkoutStore.accountPhoneNumber = fullUserData.value.phone_number;
} else {
isLogged.value = false;
user.value = null;
fullUserData.value = null;
} }
else { } catch (error) {
isLogged.value = false console.error("checkIsLogged error:", error);
user.value = null
fullUserData.value = null
}
}
catch (error) {
console.error('checkIsLogged error:', error)
} }
} }
// login // login
const email = ref() const email = ref();
const password = ref() const password = ref();
const vLogin = ref<boolean>(true) const vLogin = ref<boolean>(true);
const vCodeVerify = ref<boolean>(false) const vCodeVerify = ref<boolean>(false);
const vCode = ref<number | null>(null) const vCode = ref<number | null>(null);
async function logIn() { async function logIn() {
try { try {
const data = await useMyFetch<GenericResponse<object>>( const data = await useMyFetch<GenericResponse<object>>(
`/api/public/user/session/start`, `/api/public/user/session/start`,
{ {
method: 'POST', method: "POST",
body: JSON.stringify({ body: JSON.stringify({
mail: email.value, mail: email.value,
password: password.value, password: password.value,
}), }),
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
if (data.status === 200 || data.status === 201) { if (data.status === 200 || data.status === 201) {
console.log(vCodeVerify.value) console.log(vCodeVerify.value);
$toast.success('Code successfully sent to your email', { $toast.success("Code successfully sent to your email", {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
vLogin.value = false vLogin.value = false;
vCodeVerify.value = true vCodeVerify.value = true;
} } else {
else { $toast.error("Failed to sent code to your email. Please try again.", {
$toast.error('Failed to sent code to your email. Please try again.', {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
store.minValue = data store.minValue = data;
} } catch (error) {
catch (error) { console.error("getList error:", error);
console.error('getList error:', error)
} }
} }
const sendFormCode = async (redirect?: boolean) => { const sendFormCode = async (redirect?: boolean) => {
try { try {
await useMyFetch<GenericResponse<object>>( const data = await useMyFetch<GenericResponse<object>>(
`/api/public/user/session/confirm`, `/api/public/user/session/confirm`,
{ {
method: 'POST', method: "POST",
body: JSON.stringify({ body: JSON.stringify({
code: vCode.value, code: vCode.value,
mail: email.value, mail: email.value,
}), }),
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
onErrorOccured: (_, status) => { onErrorOccured: (_, status) => {
throw new Error(`HTTP error: ${status}`) throw new Error(`HTTP error: ${status}`);
}, },
}, }
) );
await checkIsLogged() await checkIsLogged();
if (isLogged.value) { if (isLogged.value) {
if (redirect) { if (redirect) {
console.log(isLogged.value) console.log(isLogged.value);
menuStore.navigateToItem() menuStore.navigateToItem();
} } else {
else {
// window.location.href = atob(redirect); // window.location.href = atob(redirect);
} }
} } else {
else {
useNuxtApp().$toast.error(`Error occurred: Failed to confirm code`, { useNuxtApp().$toast.error(`Error occurred: Failed to confirm code`, {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
} }
} } catch (e) {
catch (e) {
console.error(e)
useNuxtApp().$toast.error(`Invalid code provided`, { useNuxtApp().$toast.error(`Invalid code provided`, {
autoClose: 5000, autoClose: 5000,
dangerouslyHTMLString: true, dangerouslyHTMLString: true,
}) });
}
} }
};
return { return {
isLogged, isLogged,
@ -152,5 +144,5 @@ export const useUserStore = defineStore('userStore', () => {
logIn, logIn,
checkIsLogged, checkIsLogged,
sendFormCode, sendFormCode,
} };
}) });

View File

@ -1,70 +1,32 @@
export interface AddressesList { export interface AddressesList {
address: { address: {
city: string city: string;
country_iso: string country_iso: string;
name: string name: string;
postcode: string postcode: string;
street: string street: string;
surname: string surname: string;
} };
address_id: number address_id: number;
alias: string alias: string;
customer_id: number customer_id: number;
is_default: boolean is_default: boolean;
is_official: boolean is_official: boolean;
} }
export interface UserAddressOfficial { export interface UserAddressOfficial {
address: { address: {
name: string name: string;
surname: string surname: string;
street: string street: string;
postcode: string postcode: string;
city: string city: string;
country: { country: {
country_lang: [ country_lang: [
{ {
name: string name: string;
},
]
}
} }
} ];
};
export interface Payment { };
bank_name: string
city: string
country_account_number: string
country_iso: string
country_name: string
currency_iso: string
iban: string
id: number
postcode: string
street_and_number: string
swift: string
}
export interface CheckoutOrder {
cart_id: number
currency_iso: string
customer_id: number
delivery_details: {
address: CheckoutAddress
contact_email: string
contact_phone_number: string
delivery_option_id: number
}
payment_bank_account_id: number
}
export interface CheckoutAddress {
city: string
country_iso: {
str: string
}
name: string
postcode: string
street: string
surname: string
} }

View File

@ -1,26 +0,0 @@
export interface Footer {
data: {
contact: Contact
docs: FooterDoc[]
}
id: number
name: string
}
export interface Contact {
header: string
ownerAddress: string
ownerMail: string
}
export interface FooterDoc {
footer_section: string
translation: string
data: FooterPdf[]
}
export interface FooterPdf {
name: string
translation: string
guest_avaliable: boolean
}

View File

@ -20,6 +20,7 @@ export interface FrontMenuLang {
link_rewrite: string link_rewrite: string
} }
export interface UIFrontMenu extends FrontMenu { export interface UIFrontMenu extends FrontMenu {
children?: UIFrontMenu[] children?: UIFrontMenu[];
} }

View File

@ -1,68 +1,71 @@
import type { DefineComponent } from 'vue' import type { DefineComponent } from "vue";
import type { FrontSection } from '~/types/frontSection' import type { FrontSection } from "~/types/frontSection";
export type componentsListType = { export type componentsListType = {
name: string name: string;
component: FrontSection component: FrontSection;
componentInstance: DefineComponent componentInstance: DefineComponent;
} };
export type PartnersList = { export type PartnersList = {
country_iso: string country_iso: string;
country_name: string country_name: string;
total: number total: number;
} };
export type FeatureValue = { export type FeatureValue = {
parent: number parent: number;
products_with_value: number products_with_value: number;
value: string value: string;
value_id: number value_id: number;
} };
export type Feature = { export type Feature = {
feature: string feature: string;
feature_id: number feature_id: number;
feature_values: FeatureValue[] feature_values: FeatureValue[];
products_with_feature: number products_with_feature: number;
} };
export type ProductType = { export type ProductType = {
applied_tax_rate: number applied_tax_rate: number;
cover_picture_uuid: string cover_picture_uuid: string;
description: string description: string;
formatted_price: string formatted_price: string;
id: number id: number;
in_stock: number in_stock: number;
is_sale_active: boolean is_sale_active: boolean;
link_rewrite: string link_rewrite: string;
name: string name: string;
price: number price: number;
tax_name: string tax_name: string;
cart_item_id?: number cart_item_id?: number;
product_id?: number product_id?: number;
} };
export interface Country { export interface Country {
iso_code: string iso_code: string;
currency_iso_code: string currency_iso_code: string;
call_prefix: string call_prefix: string;
need_postcode: boolean need_postcode: boolean;
postcode_format: string postcode_format: string;
is_default: boolean is_default: boolean;
active: boolean active: boolean;
name: string name: string;
} }
export interface Currency { export interface Currency {
iso_code: string iso_code: string;
name: string name: string;
UpdatedAt: string UpdatedAt: string;
iso_code_num: number iso_code_num: number;
precision: number precision: number;
sign: string sign: string;
active: boolean active: boolean;
suffix: boolean suffix: boolean;
} }
export interface Language { export interface Language {
@ -77,54 +80,49 @@ export interface Language {
active: boolean active: boolean
} }
export interface CookieData { export interface CookieData { country: Country, currency: Currency, language: Language }
country: Country
currency: Currency
language: Language
}
export interface CartItem { export interface CartItem {
cart_item_id: number cart_item_id: number;
link_rewrite: string link_rewrite: string;
name: string name: string;
picture_uuid: string picture_uuid: string;
product_id: number product_id: number;
quantity: number quantity: number;
single_item_price: number single_item_price: number;
total_price: number total_price: number;
}
export interface UserCart {
cart_items: CartItem[];
id: number;
checkout_in_progress: boolean;
currency_iso: string;
total_value: number;
} }
export interface GenericResponse<Data> { export interface GenericResponse<Data> {
data: Data data: Data;
message?: string message?: string;
status: number status: number;
} }
export interface GenericResponseItems<Data> { export interface GenericResponseItems<Data> {
data: { items: Data, items_count: number } data: { items: Data; items_count: number };
message?: string message?: string;
status: number status: number;
} }
export interface GenericResponseChildren<Data> { export interface GenericResponseChildren<Data> {
data: { children: Data, items_count: number } data: { children: Data; items_count: number };
message?: string message?: string;
status: number status: number;
} }
export type { export type {
InvestmentPiece, InvestmentPiece,
PlanPrediction, PlanPrediction,
PeriodToFirstPiece, PeriodToFirstPiece,
} from './planPrediction' } from "./planPrediction";
export type { export type { Product } from "./product";
Product, export type { FrontMenu, FrontMenuLang, UIFrontMenu } from "./frontMenu";
ProductItem,
CategoryProductItem,
FeatureProductItem,
RecommendationProductItem,
CartProduct,
UserCart,
} from './product'
export type { FrontMenu, FrontMenuLang, UIFrontMenu } from './frontMenu'
export type { Contact, Footer, FooterDoc, FooterPdf } from './footer'

View File

@ -1,85 +1,13 @@
export interface Product { export interface Product {
id: number id: number;
link_rewrite: string link_rewrite: string;
name: string name: string;
cover_picture_uuid: string cover_picture_uuid: string;
description: string description: string;
price: number price: number;
formatted_price: string formatted_price: string;
in_stock: number in_stock: number;
is_sale_active: boolean is_sale_active: boolean;
applied_tax_rate: number applied_tax_rate: number;
tax_name: string tax_name: string;
}
export interface CartProduct {
cart_item_id: number
link_rewrite: string
name: string
picture_uuid: string
product_id: number
quantity: number
single_item_price: number
total_price: number
}
export interface UserCart {
cart_items: CartProduct[]
checkout_in_progress: boolean
currency_iso: string
id: number
total_value: number
}
export interface ProductItem {
id: number
cover_picture_uuid: string
name: string
price: number
applied_tax_rate: number
tax_name: string
buyout_price: number
buyout_price_reduction: number
sale_active: boolean
in_stock: number
reference: string
description: string
description_short: string
link_rewrite: string
meta_title: string
meta_description: string
weight: number
features: FeatureProductItem[]
categories: CategoryProductItem[]
picture_uuids: string[]
video_uuids: string[]
recommendations: RecommendationProductItem[]
}
export interface FeatureProductItem {
feature_id: number
feature: string
value_id: number
value: string
}
export interface CategoryProductItem {
id: number
name: string
link_rewrite: string
parent_id: number
}
export interface RecommendationProductItem {
id: number
link_rewrite: string
name: string
cover_picture_uuid: string
description: string
price: number
formatted_price: string
in_stock: number
is_sale_active: boolean
applied_tax_rate: number
tax_name: string
} }

View File

@ -1,49 +1,49 @@
export interface Customer { export interface Customer {
active: boolean active: boolean;
agreed_for_newsletter: boolean agreed_for_newsletter: boolean;
bank_accounts: { bank_accounts: {
bank_currency_iso: string bank_currency_iso: string;
bank_name: string bank_name: string;
customer_id: number customer_id: number;
iban: string iban: string;
swift: string swift: string;
verified: boolean verified: boolean;
}[] }[];
birthday_date: string birthday_date: string;
communication_languag_id: number communication_languag_id: number;
document_verified: boolean document_verified: boolean;
documents: { documents: {
file: string file: string;
id: number id: number;
name: string name: string;
size: number size: number;
typ: string typ: string;
}[] }[];
email: string email: string;
entity: { entity: {
city: string city: string;
country_iso: string country_iso: string;
customer_id: number customer_id: number;
extra_enitity_id: string extra_enitity_id: string;
name: string name: string;
national_court_register_number: string national_court_register_number: string;
postcode: string postcode: string;
statistical_number: string statistical_number: string;
street: string street: string;
vat_number: string vat_number: string;
web_pages_list: string web_pages_list: string;
}[] }[];
first_name: string first_name: string;
is_entity: boolean is_entity: boolean;
is_partner: boolean is_partner: boolean;
is_root: boolean is_root: boolean;
last_name: string last_name: string;
metadata: { metadata: {
id: number id: number;
metadata: string metadata: string;
type: string type: string;
}[] }[];
partner_code: string partner_code: string;
phone_number: string phone_number: string;
taxes_country_iso: string taxes_country_iso: string;
} }

View File

@ -2,26 +2,23 @@ const REGEX_EMAIL = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
// Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character // Minimum eight characters, at least one uppercase letter, one lowercase letter, one number and one special character
// REGEX_PASSWORD = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/, // REGEX_PASSWORD = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
// REGEX_PASSWORD = /^(?=.*[A-Za-z])(?=.*\d)(?=.*\W)[A-Za-z\d^\S]{8,}$/, // REGEX_PASSWORD = /^(?=.*[A-Za-z])(?=.*\d)(?=.*\W)[A-Za-z\d^\S]{8,}$/,
REGEX_PASSWORD = new RegExp( REGEX_PASSWORD = new RegExp(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W)[A-Za-z\d^\S]{8,}$/),
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W)[A-Za-z\d^\S]{8,}$/,
),
REGEX_CODE = /.{6}/, REGEX_CODE = /.{6}/,
// REGEX_PHONE = /^\+?[1-9][0-9]{7,14}$/, // REGEX_PHONE = /^\+?[1-9][0-9]{7,14}$/,
REGEX_PHONE = new RegExp( REGEX_PHONE = new RegExp(/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im),
/^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im,
),
REGEX_DATE = /^[+-]?\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/, REGEX_DATE = /^[+-]?\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/,
// Only numbers // Only numbers
REGEX_ONLYNUMBERS = /^[0-9]*$/, REGEX_ONLYNUMBERS = /^[0-9]*$/,
// Number (price) // Number (price)
REGEX_NUMBER = /^(?!0*[.,]0*$|[.,]0*$|0*$)\d+[,.]?\d{0,2}$/, REGEX_NUMBER = /^(?!0*[.,]0*$|[.,]0*$|0*$)\d+[,.]?\d{0,2}$/,
REGEX_NUMBER_WITH_ZERO = /^[0-9]{1,10}([.][0-9]{1,2})?$/, REGEX_NUMBER_WITH_ZERO = /^[0-9]{1,10}([.][0-9]{1,2})?$/,
REGEX_URL REGEX_URL = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
= /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
// URL // URL
// REGEX_URL = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/ // REGEX_URL = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
export { export {
REGEX_EMAIL, REGEX_EMAIL,
@ -34,3 +31,4 @@ export {
REGEX_DATE, REGEX_DATE,
REGEX_URL, REGEX_URL,
} }

View File

@ -1,17 +1,12 @@
const validation = function ( const validation = function (item:string, min:number, max:number, regEx = /.*/) {
item: string,
min: number,
max: number,
regEx = /.*/,
) {
if ( if (
item == undefined item == undefined ||
|| item.length < min item.length < min ||
|| item.length > max item.length > max ||
|| !regEx.test(item) !regEx.test(item)
) ) return false;
return false else return true;
else return true };
}
export { validation }
export { validation };