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

@ -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,
}
})
};
});