summary checkout
This commit is contained in:
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 |
@ -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,
|
||||
};
|
||||
});
|
||||
|
@ -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,
|
||||
}
|
||||
})
|
||||
};
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user