floranet/src/hooks/useCheckoutForm.js

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