floranet/src/pages/CheckoutPage.vue

895 lines
24 KiB
Vue

<script>
import { defineComponent, onBeforeMount, ref } from "vue";
import { useRouter } from "vue-router";
import Container from "src/components/ui/Container.vue";
import { useCheckoutForm } from "src/hooks/useCheckoutForm";
export default defineComponent({
name: "CheckoutPage",
components: {
Container,
},
setup() {
const { push } = useRouter();
const {
provinceOptions,
handleClickStep,
stepsFormated,
getFormatedDate,
stepList,
checkoutBlock,
cart,
totalPrice,
isError,
onError,
redsysData,
tooltip: {
postalCode: {
postalCodeRef,
postalCodeTooltip,
floatingStyles,
isHidden,
hideTooltip,
showTooltip,
},
},
formState: { errors, meta, 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,
},
phoneInputRef,
phoneSenderInputRef,
redsysFormRef,
} = useCheckoutForm();
onBeforeMount(() => {
if (cart.length === 0) return push("/");
});
return {
handleClickStep,
onSubmit,
onError,
redsysData,
phoneInputRef,
phoneSenderInputRef,
redsysFormRef,
postalCodeRef,
postalCodeTooltip,
floatingStyles,
isHidden,
hideTooltip,
showTooltip,
checkoutBlock,
stepsFormated,
getFormatedDate,
provinceOptions,
totalPrice,
stepList,
cart,
step: ref(1),
isLoadingSubmit,
successURL: ref(""),
cancelURL: ref(""),
meta,
errors,
isError,
name,
nameAttrs,
surname,
surnameAttrs,
address,
addressAttrs,
postalCode,
postalCodeAttrs,
phone,
phoneAttrs,
city,
cityAttrs,
province,
provinceAttrs,
senderName,
senderNameAttrs,
senderCifNif,
senderCifNifAttrs,
senderEmail,
senderEmailAttrs,
senderPhone,
senderPhoneAttrs,
senderNotes,
senderNotesAttrs,
terms,
termsAttrs,
paymentMethod,
paymentMethodAttrs,
};
},
});
</script>
<template>
<q-page class="checkout-page">
<Container tag="section">
<header class="header-title">
<h1 class="pege-title">¿A quién y dónde lo entregamos?</h1>
<!--<p class="page-subtitle checkout">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>-->
</header>
<div class="checkout-container">
<div class="checkout-steps">
<div v-for="({ active, description, name, value }, i) in stepsFormated" class="step-item-container" :key="i">
<div class="step-item">
<div class="circle-step-container">
<span class="border-step" :class="[i == 0 && 'transparent']" />
<div class="circle-step" :class="[
(active || (meta.valid && i == 1) || !checkoutBlock) &&
'active',
]">
<span class="step-value">{{ value }}</span>
</div>
<span class="border-step" :class="[i == stepList['data'].length - 1 && 'transparent']" />
</div>
<div class="step-content">
<div class="title">
<h4>{{ name }}</h4>
</div>
<div class="description">
<p>{{ description }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="checkout-content">
<div class="checkout-form">
<q-form method="post" id="checkout-form" @submit.prevent="onSubmit">
<div class="form-fields-container delivery">
<header class="checkout-header-form">
<h3>Instrucciones para la entrega</h3>
</header>
<div class="checkout-fields">
<div class="field-control field-input">
<q-input placeholder="Nombre*" name="name" type="text" v-model="name" v-bind:="nameAttrs"
:error="!!errors.name" :error-message="errors.name" outlined />
</div>
<div class="field-control field-input">
<q-input placeholder="Apellidos*" name="surname" type="text" v-model="surname"
v-bind:="surnameAttrs" :error="!!errors.surname" :error-message="errors.surname" outlined />
</div>
<div class="field-control field-input">
<q-input placeholder="Dirección*" name="address" type="text" v-model="address"
v-bind:="addressAttrs" :error="!!errors.address" :error-message="errors.address" outlined />
</div>
<div class="field-control field-input">
<q-input placeholder="Código postal*" name="postalCode" type="text" mask="#####"
v-model="postalCode" v-bind:="postalCodeAttrs" :error="!!errors.postalCode"
:error-message="errors.postalCode" ref="postalCodeRef" readonly outlined @mouseenter="showTooltip"
@mouseleave="hideTooltip">
<template #after>
<q-btn to="/" :style="'--clr: #ffffff'" class="btn custom-btn-input paragraph-sm"
label="EDITAR" />
</template>
</q-input>
<div ref="postalCodeTooltip" :style="[floatingStyles, '--clr: #117564']"
:class="['tooltip ', isHidden && 'hidden']" @mouseenter="showTooltip" @mouseleave="hideTooltip">
<p class="paragraph-sm">
No se puede editar este campo
<a href="https://www.google.com/maps" rel="noreferrer noopener" target="_blank"
class="paragraph-sm link">
¿No conoce su código postal?
</a>
</p>
</div>
</div>
<div class="field-control field-select">
<q-select name="province" v-model="province" v-bind:="provinceAttrs" :error="!!errors.province"
:error-message="errors.province" :options="provinceOptions" option-value="code"
option-label="name" label="País*" stack-label map-options emit-value outlined />
</div>
<div class="field-control field-select">
<q-input placeholder="Ciudad*" name="city" type="text" v-model="city" v-bind:="cityAttrs"
:error="!!errors.city" :error-message="errors.city" outlined />
</div>
<div class="field-control field-input telephone">
<vue-tel-input v-model="phone" v-bind="phoneAttrs" :styleClasses="[
'custom-input',
!!errors.phone && 'error',
]" ref="phoneInputRef" :inputOptions="{
placeholder: 'Teléfono*',
}" />
<p v-if="!!errors.phone" class="error">
{{ errors.phone }}
</p>
</div>
</div>
</div>
<div class="form-fields-container sender">
<header class="checkout-header-form">
<h3>Remitente</h3>
</header>
<div class="checkout-fields">
<div class="field-control field-input">
<q-input placeholder="Nombre y apellidos o nombre de empresa" name="senderName" type="text"
v-model="senderName" v-bind:="senderNameAttrs" :error="!!errors.senderName"
:error-message="errors.senderName" outlined />
</div>
<div class="field-control field-input">
<q-input placeholder="CIF / NIF" name="senderCifNif" type="text" v-model="senderCifNif"
v-bind:="senderCifNifAttrs" :error="!!errors.senderCifNif" :error-message="errors.senderCifNif"
outlined />
</div>
<div class="field-control field-input">
<q-input placeholder="Email" name="senderEmail" type="email" v-model="senderEmail"
v-bind:="senderEmailAttrs" :error="!!errors.senderEmail" :error-message="errors.senderEmail"
outlined />
</div>
<div class="field-control field-input telephone">
<vue-tel-input v-model="senderPhone" v-bind="senderPhoneAttrs" :styleClasses="[
'custom-input',
!!errors.senderPhone && 'error',
]" ref="phoneSenderInputRef" :inputOptions="{
placeholder: 'Teléfono*',
}" />
<p v-if="!!errors.senderPhone" class="error">
{{ errors.senderPhone }}
</p>
</div>
<div class="field-control field-input">
<q-input
placeholder="Notas sobre tu pedido (Opcional), por ejemplo, notas especiales para la entrega"
name="senderNotes" type="textarea" v-model="senderNotes" v-bind:="senderNotesAttrs"
:error="!!errors.senderNotes" :error-message="errors.senderNotes" class="message" autogrow
outlined />
</div>
</div>
</div>
</q-form>
<form v-if="paymentMethod === 'redsys' && meta.valid" name="from"
action="https://sis-t.redsys.es:25443/sis/realizarPago" method="POST" class="hide">
<input type="text" name="Ds_SignatureVersion" :value="redsysData.Ds_SignatureVersion" />
<input type="text" name="Ds_MerchantParameters" :value="redsysData.Ds_MerchantParameters" />
<input type="text" name="Ds_Signature" :value="redsysData.Ds_Signature" />
<input ref="redsysFormRef" type="submit" value="Go to pay" />
</form>
</div>
<aside class="checkout-aside">
<div class="checkout-delivery-date" :class="meta.valid && 'active'">
<header class="checkout-aside-header green-text">
<strong class="checkout-aside-title"> Fecha de entrega </strong>
</header>
<div class="checkout-delivery-body">
<p class="green-text">{{ getFormatedDate((cart.length > 0)?cart[0].dated:'') }}</p>
</div>
</div>
<div class="checkout-summary">
<header class="checkout-aside-header gray-bg">
<strong class="checkout-aside-title">
Resumen del pedido
</strong>
</header>
<div class="checkout-summary-body gray-bg">
<ul class="checkout-summary-list">
<li class="checkout-summary-item" v-for="({ name, price }, index) in cart" :key="index">
<p>
{{ name }}
<span>{{ price }}€</span>
</p>
</li>
</ul>
<p class="green-text">Envio Gratuíto</p>
</div>
<footer class="checkout-summary-footer">
<p class="checkout-summary-paragraph">Total</p>
<p class="checkout-summary-paragraph summary-price">
{{ totalPrice }}€
</p>
</footer>
</div>
<div class="checkout-payment-methods gray-bg">
<header class="checkout-aside-header">
<strong class="checkout-aside-title" :style="!!errors.paymentMethod && 'color: red'">
Método de pago
</strong>
<p v-if="!!errors.paymentMethod"></p>
</header>
<div class="checkout-payment-body">
<q-radio v-model="paymentMethod" v-bind="paymentMethodAttrs" val="paypal" color="primary">
<p>Paypal</p>
</q-radio>
<q-radio v-model="paymentMethod" v-bind="paymentMethodAttrs" val="redsys" color="primary">
<p>Tarjeta de crédito</p>
</q-radio>
</div>
</div>
<div class="checkout-terms">
<q-checkbox v-model="terms" v-bind="termsAttrs" class="terms">
<p :style="!!errors.terms && 'color: red;'">
He leído y estoy de acuerdo con los términosy condiciones de
la tienda Floranet
</p>
</q-checkbox>
<q-btn flat class="btn" type="submit" form="checkout-form" :loading="isLoadingSubmit">
PROCEDER AL PAGO
</q-btn>
</div>
</aside>
</div>
</div>
</Container>
</q-page>
</template>
<style lang="scss" scoped>
.checkout-page {
& .checkout-steps {
display: flex;
justify-content: center;
align-items: center;
@media only screen and (max-width: $med-sm) {
flex-wrap: wrap;
& .border-step {
opacity: 0;
visibility: hidden;
}
}
}
& .step-item-container {
width: min(100%, 200px);
}
& .border-step {
width: 90px;
height: 1px;
background-color: $primary-dark;
}
& .circle-step-container {
display: grid;
justify-content: center;
align-items: center;
grid-template-columns: 1fr auto 1fr;
}
& .circle-step {
width: 56px;
height: 56px;
border: 1px solid $primary-dark;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
user-select: none;
.step-value {
font-family: $font-questrial;
color: $primary-dark;
font-size: 1.25rem;
}
&.active {
background-color: $primary-dark;
.step-value {
color: $white;
}
}
}
& .step-content {
display: flex;
flex-direction: column;
align-items: center;
font-family: $font-questrial;
h4 {
font-size: 1rem;
font-weight: 700;
color: $text-default;
margin-top: 5px;
margin-bottom: 4px;
line-height: 1.3;
}
p {
font-size: 0.875rem;
color: $text-default;
font-family: $font-lora;
}
}
& .checkout-content {
width: min(100%, 1144px);
margin: 50px auto calc(146px - 72px);
display: flex;
flex-wrap: wrap;
gap: 20px;
.checkout-header-form {
background-color: $grey-700;
padding: 12px 30px;
width: 100%;
margin-bottom: 21px;
border-radius: 5px;
& h3 {
color: $text-default;
font-weight: 600;
font-size: 0.875rem;
line-height: 21px;
letter-spacing: 0.28px;
}
@media only screen and (max-width: $med-lg) {
margin-bottom: 11px;
}
}
& .checkout-form {
flex: 1 0 min(100%, 795px);
}
& .checkout-aside {
flex: 1 0 min(100%, 329px);
& .checkout-delivery-date,
& .checkout-summary,
& .checkout-payment-methods {
border-radius: 5px;
overflow: hidden;
}
& .gray-bg {
background-color: $secondary-10;
}
& .checkout-delivery-date {
display: flex;
flex-direction: column;
user-select: none;
gap: 18px;
padding: 0 23px 0;
background-color: $primary-light;
transition: 200ms ease-in;
opacity: 0;
visibility: hidden;
height: 0;
overflow: hidden;
&.active {
height: min-content;
padding-block: 16px 18px;
opacity: 1;
visibility: visible;
margin-bottom: 21px;
}
}
& .checkout-summary {
margin-bottom: 33px;
& .checkout-aside-header,
& .checkout-summary-body,
& .checkout-summary-footer {
padding-inline: 22px 19px;
}
& .checkout-aside-header {
padding-block: 16px 17px;
& .checkout-aside-title {
font-family: $font-lora;
font-weight: 600;
line-height: 21px;
letter-spacing: 0.32px;
}
}
& .checkout-summary-body {
padding-bottom: 23px;
& p {
font-size: $font-12;
line-height: 21px;
letter-spacing: 0.24px;
color: $text-default;
}
& .checkout-summary-list {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 4px;
& .checkout-summary-item p {
display: flex;
justify-content: space-between;
}
}
}
& .checkout-summary-footer {
display: flex;
justify-content: space-between;
background-color: $secondary-orange-light;
padding: 19px 19px 20px 23px;
& .checkout-summary-paragraph {
font-family: $font-lora;
line-height: 21px;
letter-spacing: 0.32px;
color: $text-default;
}
}
}
& .checkout-payment-methods {
padding: 9px 16px 20px 21px;
margin-bottom: 21px;
& .checkout-aside-header {
margin-bottom: 14px;
}
& .checkout-payment-body {
display: flex;
flex-direction: column;
gap: 12px;
& p {
width: min(100%, 200px);
display: inline-flex;
justify-content: space-between;
gap: 31px;
font-size: $font-12;
& .card-flags {
display: inline-flex;
align-items: center;
gap: 4px;
}
& a {
margin-left: 4px;
font-family: $font-questrial;
color: $blue;
font-size: $font-12;
line-height: 21px;
letter-spacing: 0.24px;
text-decoration: underline;
}
}
}
}
& .checkout-terms {
display: flex;
flex-direction: column;
gap: 23px;
& .terms p {
margin-left: 10px;
font-size: $font-12;
line-height: 17px;
letter-spacing: 0.24px;
color: $text-muted-one;
}
& .erro {
font-family: $font-lora;
font-size: $font-12;
color: #ff0000;
text-align: center;
}
}
}
@media only screen and (max-width: $med-lg) {
gap: 47px;
}
}
& .checkout-success {
width: min(100%, 499px);
margin: 122px auto 0;
text-align: center;
& .checkout-success-title {
margin-bottom: 26px;
}
& .checkout-success-body {
& .checkout-success-content {
background-color: $secondary-5;
padding: 30px 46px 42px 38px;
border-radius: 5px 5px 0px 0px;
& .checkout-success-list {
display: flex;
flex-direction: column;
gap: 28px;
& .checkout-success-item {
display: flex;
flex: 1;
& .checkout-item-content {
display: flex;
justify-content: space-between;
flex: 1;
min-height: 61px;
& .checkout-product-details {
display: flex;
gap: 14px;
& .checkout-product-img {
object-fit: cover;
width: 54px;
height: 100%;
border-radius: 5px;
}
& .checkout-product-title {
font-size: $font-12;
line-height: 21px;
letter-spacing: 0.24px;
font-family: $font-questrial;
color: $text-default;
}
}
& .checkout-product-price {
color: $text-muted-one;
font-family: $font-roboto;
font-size: $font-12;
line-height: 21px;
letter-spacing: 0.24px;
}
}
}
}
@media only screen and (max-width: $med-lg) {
padding-right: 9px;
}
}
& .checkout-success-footer {
display: flex;
justify-content: space-between;
background-color: $secondary-40;
border-radius: 0px 0px 5px 5px;
padding: 14px 46px 7px 36px;
& .checkout-success-paragraph {
font-family: $font-lora;
letter-spacing: 0.32px;
line-height: 21px;
font-weight: 600;
color: $text-muted-one;
}
}
}
}
& .form-fields-container {
display: flex;
flex-wrap: wrap;
&.delivery {}
&.sender {
margin-top: 12px;
}
& .checkout-fields {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 12px 9px;
@media only screen and (max-width: $med-lg) {
gap: 15px;
}
}
.field-control {
flex: 1 0 min(100%, 390px);
&.telephone {
flex: 0 0 calc(50% - 5px);
@media only screen and (max-width: $med-lg) {
flex: 1 0 min(100%, 390px);
}
}
&.field-input {
label {
padding-bottom: 10px;
}
& .q-field__control {
background-color: #fff;
height: 40px;
border: 1px solid $primary-light;
& input {
padding: 0px 0px 0px 20px;
font-family: $font-questrial;
color: $text-default !important;
}
& select {
font-family: $font-questrial;
color: $text-default !important;
}
&.text-negative {
border-color: $negative;
}
&::after,
&::before {
display: none;
}
}
.q-field__marginal {
height: 40px;
padding-right: 30px;
}
}
.q-field--error .q-field__bottom {
padding-top: 4px;
font-size: 0.675rem;
}
&.field-select {
.q-field__control {
min-height: initial;
background-color: #fff;
height: 40px;
padding: 0px 30px;
border: 1px solid $primary-light;
.q-field__native span {
display: none;
}
}
.q-field__append {
height: 40px;
}
.q-field__label {
font-family: $font-questrial;
color: $text-default !important;
}
.q-field__control-container {
padding-top: 0;
height: 36px;
}
}
}
@media only screen and (max-width: $med-lg) {
&.sender {
margin-top: 45px;
}
}
}
& .custom-input {
padding: 10.5px 1px;
border-radius: 4px;
transition: 200ms ease-in-out;
&:hover {
border-color: $black;
}
&:focus-within {
border-color: $primary;
box-shadow: inset 0 0 0 1px $primary;
}
&.error {
border-color: $negative;
box-shadow: inset 0 0 0 1px $negative;
}
& .vti__input::placeholder {
font-family: $font-questrial;
font-size: $font-12;
}
}
& p.error {
font-family: $font-questrial;
color: $negative;
font-size: $font-12;
padding: 8px 12px 0;
}
& .custom-btn-input {
border-radius: 4px;
}
& .q-field__native {
font-family: "Roboto" !important;
}
}
</style>