384 lines
10 KiB
JavaScript
384 lines
10 KiB
JavaScript
import { autoUpdate, useFloating } from "@floating-ui/vue";
|
|
import { toTypedSchema } from "@vee-validate/zod";
|
|
import { storeToRefs } from "pinia";
|
|
import { useForm } from "vee-validate";
|
|
import { computed, reactive, ref, watch } from "vue";
|
|
|
|
import { apiBack } from "src/boot/axios";
|
|
import { quasarNotify } from "src/functions/quasarNotify";
|
|
import { useFormStore } from "src/stores/forms";
|
|
import { checkoutSchema } from "src/utils/zod/schemas";
|
|
import { useLocalStorage } from "./useLocalStorage";
|
|
|
|
export function useCheckoutForm() {
|
|
const { addItem, getItem, removeItem } = useLocalStorage();
|
|
|
|
//! Elements ref
|
|
const postalCodeRef = ref(null);
|
|
const postalCodeTooltip = ref(null);
|
|
const phoneInputRef = ref(null);
|
|
const phoneSenderInputRef = ref(null);
|
|
const redsysFormRef = ref(null);
|
|
|
|
//! Form
|
|
const formStore = useFormStore();
|
|
const { availability: availabilityForm } = storeToRefs(formStore);
|
|
const { handleCheckoutData } = formStore;
|
|
|
|
const availability =
|
|
availabilityForm.value.dateExpired || getItem("availability");
|
|
|
|
const phoneData = ref({
|
|
country: {
|
|
name: "",
|
|
iso2: "",
|
|
dialCode: "",
|
|
priority: 0,
|
|
areaCodes: null,
|
|
},
|
|
countryCallingCode: "",
|
|
nationalNumber: "",
|
|
number: "",
|
|
countryCode: "",
|
|
valid: false,
|
|
formatted: "",
|
|
});
|
|
const phoneSenderData = ref({
|
|
country: {
|
|
name: "",
|
|
iso2: "",
|
|
dialCode: "",
|
|
priority: 0,
|
|
areaCodes: null,
|
|
},
|
|
countryCallingCode: "",
|
|
nationalNumber: "",
|
|
number: "",
|
|
countryCode: "",
|
|
valid: false,
|
|
formatted: "",
|
|
});
|
|
|
|
const provinceOptions = ref([
|
|
{ code: "es", name: "España" },
|
|
// { code: "fr", name: "Francia" },
|
|
// { code: "pt", name: "Portugal" },
|
|
]);
|
|
|
|
const { meta, errors, handleSubmit, defineField, resetForm } = useForm({
|
|
validationSchema: toTypedSchema(checkoutSchema),
|
|
initialValues: {
|
|
paymentMethod: "",
|
|
terms: false,
|
|
postalCode: availabilityForm.value.postalCode || availability.postalCode,
|
|
phone: "",
|
|
senderPhone: "",
|
|
},
|
|
});
|
|
|
|
const [name, nameAttrs] = defineField("name");
|
|
const [surname, surnameAttrs] = defineField("surname");
|
|
const [address, addressAttrs] = defineField("address");
|
|
const [postalCode, postalCodeAttrs] = defineField("postalCode");
|
|
const [phone, phoneAttrs] = defineField("phone", {
|
|
validateOnModelUpdate: false,
|
|
});
|
|
const [city, cityAttrs] = defineField("city");
|
|
const [province, provinceAttrs] = defineField("province");
|
|
const [senderName, senderNameAttrs] = defineField("senderName");
|
|
const [senderCifNif, senderCifNifAttrs] = defineField("senderCifNif");
|
|
const [senderEmail, senderEmailAttrs] = defineField("senderEmail");
|
|
const [senderPhone, senderPhoneAttrs] = defineField("senderPhone", {
|
|
validateOnModelUpdate: false,
|
|
});
|
|
const [senderNotes, senderNotesAttrs] = defineField("senderNotes");
|
|
const [paymentMethod, paymentMethodAttrs] = defineField("paymentMethod");
|
|
const [terms, termsAttrs] = defineField("terms");
|
|
|
|
//! Tooltip hook
|
|
const { floatingStyles } = useFloating(postalCodeRef, postalCodeTooltip, {
|
|
placement: "top-start",
|
|
whileElementsMounted: autoUpdate,
|
|
});
|
|
|
|
const isHidden = ref(true);
|
|
const hideTooltip = () => {
|
|
isHidden.value = true;
|
|
};
|
|
const showTooltip = () => {
|
|
isHidden.value = false;
|
|
};
|
|
|
|
// TODO hacer el await de las provincias
|
|
/**
|
|
* const provinceOptions = getProvinces();
|
|
* onBeforeMount(async () => {});
|
|
* */
|
|
|
|
watch(
|
|
[
|
|
() => phoneInputRef.value?.modelValue,
|
|
() => phoneSenderInputRef.value?.modelValue,
|
|
],
|
|
([a, b]) => {
|
|
phoneData.value = phoneInputRef.value.phoneObject;
|
|
phoneSenderData.value = phoneSenderInputRef.value.phoneObject;
|
|
}
|
|
);
|
|
|
|
const stepActive = reactive({ data: 1 });
|
|
const stepList = reactive({
|
|
data: [
|
|
{
|
|
value: 1,
|
|
name: "Paso 1",
|
|
description: "Datos de facturación",
|
|
active: true,
|
|
},
|
|
{
|
|
value: 2,
|
|
name: "Paso 2",
|
|
description: "Confirmación",
|
|
active: false,
|
|
},
|
|
{
|
|
value: 3,
|
|
name: "Paso 3",
|
|
description: "Pago",
|
|
active: false,
|
|
},
|
|
],
|
|
});
|
|
const stepsFormated = computed(() => {
|
|
return stepList["data"].map((step) => {
|
|
if (step.value === stepActive["data"]) {
|
|
return { ...step, active: true };
|
|
}
|
|
return step;
|
|
});
|
|
});
|
|
const isError = ref(false);
|
|
const onError = () => {
|
|
isError.value = true;
|
|
};
|
|
|
|
const handleClickStep = (value) => {
|
|
stepActive["data"] = value;
|
|
};
|
|
|
|
const checkoutBlock = ref(true);
|
|
const cart = getItem("cart");
|
|
const totalPrice = computed(() => {
|
|
return cart?.reduce((acc, { price }) => {
|
|
if (price) {
|
|
//const priceWithoutLetter = price?.replace("€", "");
|
|
return +price + acc;
|
|
}
|
|
}, 0);
|
|
});
|
|
|
|
const redsysData = ref({
|
|
Ds_MerchantParameters: "",
|
|
Ds_Signature: "",
|
|
Ds_SignatureVersion: "",
|
|
orderId: null,
|
|
});
|
|
|
|
const isLoadingSubmit = ref(false);
|
|
const isErrorSubmit = ref(false);
|
|
|
|
/**
|
|
* Handles the fetching of the payment method.
|
|
*
|
|
* @param {string} type - The type of payment method paypal or redsys are valid!.
|
|
* @param {Object} values - The values needed for the payment method.
|
|
* @returns {Promise<void>} - A promise that resolves when the payment method is fetched.
|
|
*/
|
|
const handleFetchPaymentMethod = async ({ type, values }) => {
|
|
|
|
try {
|
|
const productsId = cart.map((item) => item.id);
|
|
const cartItensData = cart.map(({ id, message, ...rest }) => ({
|
|
id,
|
|
message: message || "",
|
|
}));
|
|
const deliveryData = {
|
|
customerData: {
|
|
custumerName: `${values.name} ${values.surname}`,
|
|
email: values.senderEmail,
|
|
custumerPhone: phoneData.value.number,
|
|
},
|
|
itemData: cartItensData,
|
|
deliveryData: {
|
|
dated: availability.dateExpired,
|
|
deliveryName: values.senderName,
|
|
address: values.address,
|
|
postalCode: availability.postalCode,
|
|
deliveryPhone: phoneSenderData.value.number,
|
|
deliveryMessage: values.senderNotes,
|
|
},
|
|
};
|
|
|
|
const customerName = `${values.name} ${values.surname}`;
|
|
|
|
addItem("costumer", deliveryData);
|
|
const productData = {
|
|
products: productsId,
|
|
dateExpired: availability.dateExpired,
|
|
postalCode: postalCode.value,
|
|
customer: {
|
|
customerData: {
|
|
customerName,
|
|
email: values.senderEmail,
|
|
customerPhone: phoneData.value.number,
|
|
message: values.senderNotes,
|
|
deliveryName: values.senderName || customerName,
|
|
address: values.address,
|
|
deliveryPhone:
|
|
phoneSenderData.value.number || phoneData.value.number,
|
|
},
|
|
},
|
|
type: values.paymentMethod,
|
|
};
|
|
addItem("payment", values.paymentMethod);
|
|
const typeObj = {
|
|
paypal: async () => {
|
|
const {
|
|
data: { data },
|
|
} = await apiBack.post("payment", productData);
|
|
|
|
location.href = data.link;
|
|
},
|
|
redsys: async () => {
|
|
const {
|
|
data: { data },
|
|
} = await apiBack.post("payment", productData);
|
|
redsysData.value = await data;
|
|
document.querySelector("input[name='Ds_SignatureVersion']").value = redsysData.value.Ds_SignatureVersion;
|
|
document.querySelector("input[name='Ds_MerchantParameters']").value = redsysData.value.Ds_MerchantParameters;
|
|
document.querySelector("input[name='Ds_Signature']").value = redsysData.value.Ds_Signature;
|
|
redsysFormRef.value.click();
|
|
},
|
|
default: () => {
|
|
console.error(
|
|
`FATAL ERROR ::: Payment method not found, TYPE: ${type}`
|
|
);
|
|
},
|
|
};
|
|
|
|
const paymentMethod = typeObj[type] || typeObj["default"];
|
|
await paymentMethod();
|
|
|
|
// removeItem("cart");
|
|
// removeItem("availability");
|
|
} catch (error) {
|
|
console.error(`FATAL ERROR ::: ${error}`);
|
|
|
|
quasarNotify({
|
|
type: "erro",
|
|
message:
|
|
"Se produjo un error al procesar tu compra, inténtalo de nuevo.",
|
|
});
|
|
|
|
isErrorSubmit.value = true;
|
|
} finally {
|
|
isLoadingSubmit.value = false;
|
|
handleCheckoutData(values);
|
|
resetForm();
|
|
}
|
|
};
|
|
|
|
const onSuccess = async (values, actions) => {
|
|
const INVALID_NUMBER =
|
|
"Número no válido introducido, por favor, compruébelo e inténtelo de nuevo";
|
|
|
|
if (!phoneData.value.valid) {
|
|
actions.setFieldError("phone", INVALID_NUMBER);
|
|
|
|
return;
|
|
}
|
|
|
|
if (values.senderPhone.length > 0 && !phoneSenderData.value.valid) {
|
|
actions.setFieldError("senderPhone", INVALID_NUMBER);
|
|
|
|
return;
|
|
}
|
|
|
|
isLoadingSubmit.value = true;
|
|
stepsFormated.value[1].active = true;
|
|
|
|
await handleFetchPaymentMethod({
|
|
type: values.paymentMethod,
|
|
values,
|
|
});
|
|
};
|
|
|
|
const onSubmit = handleSubmit(onSuccess);
|
|
|
|
return {
|
|
handleClickStep,
|
|
provinceOptions,
|
|
stepsFormated,
|
|
stepList,
|
|
checkoutBlock,
|
|
cart,
|
|
totalPrice,
|
|
isError,
|
|
redsysData,
|
|
|
|
phoneInputRef,
|
|
phoneSenderInputRef,
|
|
redsysFormRef,
|
|
|
|
phone: { phoneData, phoneSenderData },
|
|
|
|
onError,
|
|
tooltip: {
|
|
postalCode: {
|
|
postalCodeRef,
|
|
postalCodeTooltip,
|
|
floatingStyles,
|
|
isHidden,
|
|
hideTooltip,
|
|
showTooltip,
|
|
},
|
|
},
|
|
formState: {
|
|
meta,
|
|
errors,
|
|
onSubmit,
|
|
isLoadingSubmit,
|
|
},
|
|
fields: {
|
|
name,
|
|
nameAttrs,
|
|
surname,
|
|
surnameAttrs,
|
|
address,
|
|
addressAttrs,
|
|
postalCode,
|
|
postalCodeAttrs,
|
|
phone,
|
|
phoneAttrs,
|
|
city,
|
|
cityAttrs,
|
|
province,
|
|
provinceAttrs,
|
|
senderName,
|
|
senderNameAttrs,
|
|
senderCifNif,
|
|
senderCifNifAttrs,
|
|
senderEmail,
|
|
senderEmailAttrs,
|
|
senderPhone,
|
|
senderPhoneAttrs,
|
|
senderNotes,
|
|
senderNotesAttrs,
|
|
paymentMethod,
|
|
paymentMethodAttrs,
|
|
terms,
|
|
termsAttrs,
|
|
},
|
|
};
|
|
}
|