summary checkout

This commit is contained in:
2025-07-02 15:55:13 +02:00
parent 90e1d70f64
commit 5d59059474
7 changed files with 581 additions and 174 deletions

View File

@ -32,30 +32,11 @@
</ClientOnly>
<div class="w-full flex items-center justify-between">
<div class="flex items-center gap-[30px]">
<p
class="cursor-pointer"
@click="
menuStore.navigateToItem(
menuStore.menuItems?.find((item) => item.id === 13),
)
"
>
button
</p>
<div>
<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"
>
<div>
<i v-if="!userStore.isLogged"
@click="menuStore.navigateToItem(menuStore.menuItems?.find((item) => item.id === 11))"
class="uil uil-user text-[31px] cursor-pointer"></i>
<div v-else class="py-[6px] px-3 border border-block rounded-sm">
{{ userStore.user }}
</div>
</div>

View File

@ -9,8 +9,160 @@
<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 class="grid grid-cols-3 gap-[30px]">
<div class="col-start-1 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-[50px] space-25-55">
<div v-for="(item, index) in checkoutStore.products" :key="index">
<div class="flex items-center h-[150px]">
<div class="min-w-[150px] flex items-center justify-center 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="flex 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-xl">
<span>{{ checkoutStore.defaultAddress?.address.name }} {{
checkoutStore.defaultAddress?.address.surname }}</span>
<span>{{ checkoutStore.defaultAddress?.address.street }}</span>
<span>{{ checkoutStore.defaultAddress?.address.postcode }} {{
checkoutStore.defaultAddress?.address.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 v-model="checkoutStore.vNote"
class="border-2 border-block rounded-[8px] p-3 w-full flex-1 resize-none placeholder:text-button"
name="rty" id="1" :placeholder="component.front_section_lang[0].data.note"></textarea>
</div>
</div>
<div class="col-start-1 col-end-3 space-y-5">
<h4 class="h4-uppercase-bold-inter">Typ doručení</h4>
<div class="border-2 border-block rounded-[15px] p-[50px] space-y-[25px] text-xl">
<div @click="checkoutStore.setCurrentDelivery(delivery)"
v-for="delivery in checkoutStore.deliveryOption" class="flex flex-col cursor-pointer">
<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-[12px] 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-[15px] flex-1 flex flex-col gap-1 text-xl overflow-auto">
<UCarousel :prevIcon="'i-lucide-chevron-left'" :nextIcon="'i-lucide-chevron-right'" :ui="{
viewport: 'h-full',
container: 'h-full',
item: 'h-full',
prev: 'ring-0 text-button disable:text-block p-0 ring-inset ring-accented bg-inherit disabled:bg-inherit',
next: '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 max-w-full mx-10 h-full">
<template #default="{ item }">
<div class="flex flex-col items-start justify-between h-full">
<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] text-xl">
<div class="flex items-center justify-between">
<p>{{ component.front_section_lang[0].data.subtotal }}</p>
<p>5,043.18</p>
</div>
<div class="flex items-center justify-between">
<p>{{ component.front_section_lang[0].data.shipping_cost }}</p>
<p>5,043.18</p>
</div>
<div class="flex items-center justify-between uppercase">
<p>{{ component.front_section_lang[0].data.total }}</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">
5,043.18
</p>
</div>
</div>
<div class="flex justify-between mt-1">
<div class="flex flex-col gap-1 text-white w-full">
<div class="flex gap-3 text-black dark:text-white items-center">
<input v-model="checkoutStore.vLegal" type="checkbox" id="first" 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-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 v-model="checkoutStore.vTerms" type="checkbox" id="second" 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-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">
{{ $t("Buy") }}
</UiButtonArrow>
</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"
@ -87,9 +239,43 @@
</template>
<script setup lang="ts">
const checkoutStore = useCheckoutStore()
import { UCarousel } from '#components';
const checkoutStore = useCheckoutStore();
const productStore = useProductStore()
const menuStore = useMenuStore()
checkoutStore.getOrder()
checkoutStore.getBankAccount()
checkoutStore.getUserCart()
checkoutStore.getDeliveryOptions()
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

@ -64,4 +64,11 @@ const props = defineProps({
const productStore = useProductStore()
const menuStore = useMenuStore()
const isError = ref(false);
function handleImageError(event: Event) {
isError.value = true;
(event.target as HTMLImageElement).src = '/photo.svg';
}
</script>

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">
<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"/>
<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"/>
</svg>

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

View File

@ -1,14 +1,25 @@
import { validation } from '../utils/validation'
import { REGEX_PHONE } from '../utils/regex'
import type { GenericResponse, GenericResponseItems, UserCart } from '~/types'
import type { AddressesList, UserAddressOfficial } from '~/types/checkout'
import type { CartProduct } from '~/types/product'
import type { GenericResponse, GenericResponseItems, UserCart } from "~/types";
import type {
Address,
AddressesList,
CheckoutOrder,
Payment,
UserAddressOfficial,
} from "~/types/checkout";
import { validation } from "../utils/validation";
import { REGEX_PHONE } from "../utils/regex";
import type { CartProduct } from "~/types/product";
export const useCheckoutStore = defineStore('checkoutStore', () => {
const { $toast } = useNuxtApp()
const menuStore = useMenuStore()
const selectedIso = ref(menuStore.selectedCountry)
const showSummary = ref(false)
export const useCheckoutStore = defineStore("checkoutStore", () => {
const { $toast } = useNuxtApp();
const menuStore = useMenuStore();
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
const addressesList = ref<AddressesList[]>()
@ -141,8 +152,8 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
}
const phoneValidation = ref<boolean | null>(null)
const userStore = useUserStore()
// send form
// send checkout form
const userStore = useUserStore();
async function sendForm() {
const phoneNum = vUseAccountPhoneNumber.value
? accountPhoneNumber.value
@ -189,12 +200,12 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
$toast.success('Form successfully sent', {
autoClose: 5000,
dangerouslyHTMLString: true,
})
// redirectToSummary();
showSummary.value = true
}
else {
$toast.error('Failed to send form. Please try again.', {
});
menuStore.navigateToItem(
menuStore.menuItems?.find((item) => item.id === 13)
);
} else {
$toast.error("Failed to send form. Please try again.", {
autoClose: 5000,
dangerouslyHTMLString: true,
})
@ -209,6 +220,7 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
activeAddress.value = item
}
// get checkout
async function getCheckout() {
try {
await useMyFetch<GenericResponse<object>>(
@ -269,19 +281,20 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
const shippingPrice = ref()
async function getDeliveryOptions() {
try {
const { data } = await useMyFetch<
GenericResponseItems<{
items: [
{
country_iso: string
country_name: string
delivery_supplier_id: number
delivery_supplier_name: string
id: number
shippment_price: string
},
]
}>
const res = await useMyFetch<
GenericResponseItems<object>
// {
// items: [
// {
// country_iso: string;
// country_name: string;
// delivery_supplier_id: number;
// delivery_supplier_name: string;
// id: number;
// shippment_price: string;
// }
// ];
// }
>(`/api/restricted/cart/checkout/delivery-options`, {
headers: {
'Content-Type': 'application/json',
@ -294,18 +307,44 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
},
})
console.log(data)
deliveryOption.value = data.items
currentDelivery.value = data.items[0]
shippingPrice.value = data.items[0].shippment_price
fullPrice.value = Number(fullPrice.value) + Number(shippingPrice.value)
}
catch (error) {
console.error('getUserCart error:', error)
const data = {
items: [
{
id: 31,
shippment_price: "2",
delivery_supplier_id: 4,
delivery_supplier_name: "Personal collection",
country_iso: "pl",
country_name: "Poland",
},
{
id: 34,
shippment_price: "20",
delivery_supplier_id: 1,
delivery_supplier_name: "Ceska Posta",
country_iso: "pl",
country_name: "Poland",
},
],
items_count: 2,
};
deliveryOption.value = data.items;
currentDelivery.value = data.items[0];
shippingPrice.value = data.items[0].shippment_price;
fullPrice.value = Number(fullPrice.value) + Number(shippingPrice.value);
} catch (error) {
console.error("getUserCart error:", error);
}
}
const defaultAddress = ref()
const setCurrentDelivery = (item: any) => {
shippingPrice.value = item.shippment_price;
currentDelivery.value = item;
fullPrice.value = Number(fullPrice.value) + Number(shippingPrice.value);
};
const defaultAddress = ref();
async function getDefAddress() {
try {
const { data } = await useMyFetch<
@ -337,6 +376,137 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
}
}
// get bank data
const paymentMethods = ref([] as Payment[]);
const fullAddress = ref<Address>();
const currentPayment = ref<Payment | null>(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;
console.log(fullAddress.value);
} catch (error) {
console.error("getOrder error:", error);
}
}
async function setNewAddress(index: number) {
currentPayment.value = paymentMethods.value.find(
(item, index) => index === index
);
}
// send summary form
async function sendSummaryForm() {
vLegal.value
? (legalValidation.value = false)
: (legalValidation.value = true);
vTerms.value
? (termsValidation.value = false)
: (termsValidation.value = true);
// if (vTerms.value && vLegal.value) {
// isModalOpen.value = true;
// }
try {
const res = 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}`,
});
},
}
);
putCheckoutBankAccount();
} catch (error) {
console.error("uploadAddress error:", error);
}
}
// put checkout bank-account
async function putCheckoutBankAccount() {
try {
const res = await useMyFetch<GenericResponse<object>>(
`restricted/cart/checkout/bank-account/${currentPayment.value?.id}`,
{
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}`,
});
},
}
);
} catch (error) {
console.error("uploadAddress error:", error);
}
}
return {
addressesList,
activeAddress,
@ -361,7 +531,6 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
vNewAddressCode,
vNewAddressCity,
vNewAddressCountry,
showSummary,
products,
fullPrice,
@ -370,6 +539,14 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
currentDelivery,
shippingPrice,
defaultAddress,
paymentMethods,
currentPayment,
vLegal,
vTerms,
vNote,
legalValidation,
termsValidation,
changePrefix,
getCheckout,
@ -381,5 +558,9 @@ export const useCheckoutStore = defineStore('checkoutStore', () => {
getUserCart,
getDeliveryOptions,
getDefAddress,
}
})
setCurrentDelivery,
getBankAccount,
getOrder,
setNewAddress,
};
});

View File

@ -1,4 +1,4 @@
import { useStore } from './store'
import { useStore } from "./store";
import type {
Country,
Currency,
@ -7,78 +7,78 @@ import type {
GenericResponseItems,
Language,
UIFrontMenu,
} from '~/types'
import { useMyFetch } from '#imports'
} from "~/types";
import { useMyFetch } from "#imports";
function buildTreeRecursive(
data: (FrontMenu | UIFrontMenu)[],
parentId: number,
parentId: number
): UIFrontMenu[] {
const children = data.filter(
(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,
children: buildTreeRecursive(data, item.id),
}))
}));
}
export const useMenuStore = defineStore('menuStore', () => {
const store = useStore()
const { $i18n } = useNuxtApp()
export const useMenuStore = defineStore("menuStore", () => {
const store = useStore();
const { $i18n } = useNuxtApp();
// const session = useSession();
const { $session } = useNuxtApp()
const router = useRouter()
const route = useRoute()
const { $session } = useNuxtApp();
const router = useRouter();
const route = useRoute();
const openMenu = ref(false)
const openDropDown = ref(false)
const openMenu = 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
const selectedCountry = ref({} as Country)
const selectedPhoneCountry = ref({} as Country)
const selectedCurrency = ref({} as Currency)
const selectedLanguage = ref({} as Language)
const selectedCountry = ref({} as Country);
const selectedPhoneCountry = ref({} as Country);
const selectedCurrency = ref({} as Currency);
const selectedLanguage = ref({} as Language);
const countries = ref([] as Country[])
const currencies = ref([] as Currency[])
const languages = ref([] as Language[])
const countries = ref([] as Country[]);
const currencies = ref([] as Currency[]);
const languages = ref([] as Language[]);
const getLocales = async () => {
const { data: countriesList } = await useMyFetch<
GenericResponse<Country[]>
>(`/api/public/country/list`)
countries.value = countriesList
>(`/api/public/country/list`);
countries.value = countriesList;
selectedCountry.value = countriesList.find(
country => country.iso_code === $session.currentCountryIso.value,
) as Country
(country) => country.iso_code === $session.currentCountryIso.value
) as Country;
selectedPhoneCountry.value = countriesList.find(
country => country.iso_code === $session.currentCountryIso.value,
) as Country
(country) => country.iso_code === $session.currentCountryIso.value
) as Country;
const { data: currenciesList } = await useMyFetch<
GenericResponseItems<Currency[]>
>(`/api/public/currencies`)
currencies.value = currenciesList.items
>(`/api/public/currencies`);
currencies.value = currenciesList.items;
selectedCurrency.value = currenciesList.items.find(
currency => currency.iso_code === $session.currentCurrencyIso.value,
) as Currency
(currency) => currency.iso_code === $session.currentCurrencyIso.value
) as Currency;
const { data: languagesList } = await useMyFetch<
GenericResponseItems<Language[]>
>(`/api/public/languages`)
languages.value = languagesList.items
>(`/api/public/languages`);
languages.value = languagesList.items;
selectedLanguage.value = languagesList.items.find(
language => language.iso_code === $session.currentLanguageIso.value,
) as Language
}
(language) => language.iso_code === $session.currentLanguageIso.value
) as Language;
};
const loadMenu = async () => {
try {
@ -86,80 +86,81 @@ export const useMenuStore = defineStore('menuStore', () => {
`/api/public/front/menu`,
{
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
defaultMenu.value = data.find(item => item.is_default)
const root = data.find((item) => item.is_root) as UIFrontMenu;
defaultMenu.value = data.find((item) => item.is_default);
if (root) {
menu.value = buildTreeRecursive(data, root.id)
}
else {
console.warn('Root menu item not found')
menu.value = []
menu.value = buildTreeRecursive(data, root.id);
} else {
console.warn("Root menu item not found");
menu.value = [];
}
} catch (error) {
console.log(error);
}
catch (error) {
console.log(error)
}
}
};
const navigateToItem = (item?: UIFrontMenu) => {
if (item) {
router.push({
params: { slug: item.front_menu_lang[0].link_rewrite, id: item.id },
name: `id-slug___${$i18n.locale.value}`,
})
openDropDown.value = false
}
else {
});
openDropDown.value = false;
} else {
router.push({
params: {
slug: defaultMenu.value.front_menu_lang[0].link_rewrite,
id: defaultMenu.value.id,
},
name: `id-slug___${$i18n.locale.value}`,
})
});
}
}
};
function navigateToShop() {
navigateToItem(menuItems.value?.find(item => item.id === 5))
navigateToItem(menuItems.value?.find((item) => item.id === 5));
}
function getProductMenu() {
return menuItems.value?.find(item => item.id === 13)
return menuItems.value?.find((item) => item.id === 13);
}
const getFirstImage = (size: 'l' | 'm' | 's' = 'm', needbaseurl: boolean) => {
const req = useRequestEvent()
const url = useRequestURL()
const getFirstImage = (size: "l" | "m" | "s" = "m", needbaseurl: boolean) => {
const req = useRequestEvent();
const url = useRequestURL();
// let img = "";
const img: string[] = []
const img: string[] = [];
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(
`/api/public/file/${store.components[s].front_section.img[0]}_${size}.webp`,
)
if (img.length > 0) break
`/api/public/file/${store.components[s].front_section.img[0]}_${size}.webp`
);
if (img.length > 0) break;
}
if (img.length > 0) {
if (needbaseurl) {
return `${req?.headers.get('x-forwarded-proto') || url.protocol}://${req?.headers.get('x-forwarded-host') || url.host || req?.headers.get('host')}${img[0]}`
return `${req?.headers.get("x-forwarded-proto") || url.protocol}://${
req?.headers.get("x-forwarded-host") ||
url.host ||
req?.headers.get("host")
}${img[0]}`;
}
return img[0]
return img[0];
}
return ''
}
return "";
};
const headMeta = computed(() => {
const item = menuItems.value?.find(
item => item.id.toString() === route.params.id,
)
(item) => item.id.toString() === route.params.id
);
const meta = {
title: item?.front_menu_lang[0].meta_title,
@ -168,66 +169,77 @@ export const useMenuStore = defineStore('menuStore', () => {
},
link: [
// { rel: "manifest", href: "/api/manifest.json" }
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.png' },
{ rel: "icon", type: "image/x-icon", href: "/favicon.png" },
],
script: [
{
src: 'https://leiadmin.com/leitag.js?lei=894500UT83EISNNA8D04&color=dark',
src: "https://leiadmin.com/leitag.js?lei=894500UT83EISNNA8D04&color=dark",
defer: true,
},
],
meta: [
{
hid: 'description',
name: 'description',
hid: "description",
name: "description",
content: item?.front_menu_lang[0].meta_description,
},
{
property: 'og:title',
property: "og:title",
content: item?.front_menu_lang[0].meta_title,
},
{
property: 'og:description',
property: "og:description",
content: item?.front_menu_lang[0].meta_description,
},
{
property: 'og:image',
content: getFirstImage('m', true),
property: "og:image",
content: getFirstImage("m", true),
},
{
property: 'twitter:title',
property: "twitter:title",
content: item?.front_menu_lang[0].meta_title,
},
{
property: 'twitter:description',
property: "twitter:description",
content: item?.front_menu_lang[0].meta_description,
},
{
property: 'twitter:image',
content: getFirstImage('m', true),
property: "twitter:image",
content: getFirstImage("m", true),
},
],
}
};
const preload = getFirstImage('l', false)
const preload = getFirstImage("l", false);
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
watch(
() => $session.cookieData,
async () => {
await getLocales()
await loadMenu()
await store.getMinValue()
await store.getCalculator()
await getLocales();
await loadMenu();
await store.getMinValue();
await store.getCalculator();
},
{ deep: true },
)
{ deep: true }
);
return {
menu,
@ -243,10 +255,11 @@ export const useMenuStore = defineStore('menuStore', () => {
selectedLanguage,
defaultMenu,
headMeta,
navigateToShop,
loadMenu,
navigateToItem,
getLocales,
getProductMenu,
}
})
};
});

View File

@ -30,3 +30,42 @@ export interface UserAddressOfficial {
}
}
}
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: Address;
contact_email: string;
contact_phone_number: string;
delivery_option_id: number;
note: string;
};
payment_bank_account_id: number;
}
export interface Address {
city: string;
country_iso: {
str: string;
};
name: string;
postcode: string;
street: string;
surname: string;
}