mas cambios

This commit is contained in:
Jaume Solís 2023-12-19 11:52:54 +01:00
parent 177bc3f495
commit 0f5b2627ec
16 changed files with 874 additions and 219 deletions

View File

@ -0,0 +1,14 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="21"
height="14"
viewBox="0 0 21 14"
fill="none"
>
<path
d="M0.934292 6.39147H18.2151L13.2101 1.23843C13.0946 1.13258 13.03 0.985578 13.03 0.828772C13.03 0.671966 13.0946 0.524961 13.2101 0.419117C13.3079 0.301512 13.4546 0.23291 13.6092 0.23291C13.7638 0.23291 13.9086 0.301512 14.0064 0.419117L20.0034 6.59335C20.1149 6.70116 20.1814 6.84816 20.1854 7.00301C20.1814 7.15786 20.1169 7.30486 20.0034 7.41071L14.0044 13.5889C13.9047 13.7025 13.7638 13.7672 13.6131 13.7672C13.4625 13.7672 13.3216 13.7025 13.2218 13.5889C13.1064 13.483 13.0418 13.336 13.0418 13.1792C13.0418 13.0224 13.1064 12.8754 13.2218 12.7696L18.1798 7.67335H0.934292C0.867767 7.67335 0.814941 7.62043 0.814941 7.55379V6.50907C0.814941 6.47771 0.82668 6.44635 0.850159 6.42479C0.871682 6.40323 0.902986 6.39147 0.934292 6.39147Z"
fill="white"
/>
</svg>
</template>

View File

@ -0,0 +1,7 @@
<template><svg width="29" height="19" viewBox="0 0 29 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="29" height="19" rx="3" fill="#01326F"/>
<path d="M17.1001 14.1905H11.748V4.81104H17.1001V14.1905Z" fill="#6C6BBD"/>
<path d="M12.0917 9.49997C12.0917 7.59731 13.0052 5.90247 14.4279 4.81025C13.3875 4.01156 12.0746 3.53485 10.6477 3.53485C7.26957 3.53485 4.53125 6.20549 4.53125 9.49997C4.53125 12.7945 7.26957 15.4651 10.6477 15.4651C12.0746 15.4651 13.3875 14.9884 14.4279 14.1897C13.0052 13.0975 12.0917 11.4026 12.0917 9.49997Z" fill="#0099DF"/>
<path d="M24.3189 9.49997C24.3189 12.7945 21.5806 15.4651 18.2025 15.4651C16.7756 15.4651 15.4626 14.9884 14.4219 14.1897C15.8449 13.0975 16.7585 11.4026 16.7585 9.49997C16.7585 7.59731 15.8449 5.90247 14.4219 4.81025C15.4626 4.01156 16.7756 3.53485 18.2025 3.53485C21.5806 3.53485 24.3189 6.20549 24.3189 9.49997Z" fill="#EB001B"/>
</svg>
</template>

View File

@ -0,0 +1,54 @@
<template>
<svg
width="29"
height="19"
viewBox="0 0 29 19"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_79_1742)">
<rect width="29" height="19" rx="3" fill="#1D71B9" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M17.4941 15.4503V9.1377L29.0158 9.14802V10.8917L27.684 12.2791L29.0158 13.6799V15.4603H26.8897L25.7598 14.2445L24.6377 15.4652L17.4941 15.4503Z"
fill="white"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M18.2637 14.7579V9.83228H22.5468V10.967H19.6508V11.7373H22.4778V12.8533H19.6508V13.6098H22.5468V14.7579H18.2637Z"
fill="#1D71B9"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M22.5254 14.7578L24.895 12.2923L22.5254 9.83252H24.3594L25.8076 11.3935L27.2598 9.83252H29.0154V9.87113L26.696 12.2923L29.0154 14.6882V14.7578H27.2422L25.7684 13.1811L24.3096 14.7578H22.5254Z"
fill="#1D71B9"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M18.0319 3.53491H20.8092L21.7846 5.69494V3.53491H25.2135L25.8047 5.15321L26.3979 3.53491H29.016V9.84716H15.1797L18.0319 3.53491Z"
fill="white"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M18.5578 4.2207L16.3164 9.14212H17.8536L18.2763 8.15654H20.5674L20.9901 9.14212H22.5653L20.3334 4.2207H18.5578ZM18.7503 7.05206L19.4222 5.48537L20.0938 7.05206H18.7503Z"
fill="#1D71B9"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M22.5449 9.14139V4.21997L24.7055 4.22723L25.8174 7.2528L26.9367 4.21997H29.0161V9.14139L27.6781 9.15286V5.77213L26.4149 9.14139H25.1932L23.9038 5.76066V9.14139H22.5449Z"
fill="#1D71B9"
/>
</g>
<defs>
<clipPath id="clip0_79_1742">
<rect width="29" height="19" rx="3" fill="white" />
</clipPath>
</defs>
</svg>
</template>

View File

@ -0,0 +1,28 @@
<template>
<svg
width="29"
height="19"
viewBox="0 0 29 19"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="29" height="19" rx="3" fill="url(#paint0_linear_79_1437)" />
<path
d="M14.8733 8.37561C14.8609 9.32964 15.7452 9.86206 16.4114 10.1786C17.0958 10.5034 17.3257 10.7116 17.3231 11.002C17.3179 11.4466 16.7771 11.6427 16.271 11.6504C15.388 11.6637 14.8746 11.4179 14.4665 11.2319L14.1484 12.6834C14.5579 12.8674 15.3161 13.0279 16.1025 13.0349C17.9481 13.0349 19.1557 12.1465 19.1623 10.7689C19.1694 9.02076 16.6824 8.92395 16.6994 8.14252C16.7053 7.90561 16.9371 7.65277 17.4453 7.58845C17.6967 7.55597 18.3909 7.53113 19.1779 7.88459L19.4868 6.48031C19.0636 6.33001 18.5196 6.18608 17.8423 6.18608C16.1051 6.18608 14.8831 7.0866 14.8733 8.37561ZM22.4552 6.30708C22.1182 6.30708 21.8341 6.49878 21.7074 6.79301L19.0708 12.9317H20.9152L21.2822 11.9427H23.5361L23.749 12.9317H25.3745L23.956 6.30708H22.4552ZM22.7132 8.09667L23.2454 10.5843H21.7877L22.7132 8.09667ZM12.6371 6.30708L11.1833 12.9317H12.9408L14.394 6.30708H12.6371ZM10.0371 6.30708L8.2078 10.8161L7.46784 6.98216C7.38098 6.55418 7.0381 6.30708 6.65734 6.30708H3.6668L3.625 6.49941C4.23891 6.62933 4.93642 6.83886 5.35898 7.06304C5.61761 7.19996 5.69141 7.31969 5.77631 7.64513L7.17786 12.9317H9.03528L11.8828 6.30708H10.0371Z"
fill="white"
/>
<defs>
<linearGradient
id="paint0_linear_79_1437"
x1="13.0273"
y1="19"
x2="18.7509"
y2="0.330809"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#222357" />
<stop offset="1" stop-color="#254AA5" />
</linearGradient>
</defs>
</svg>
</template>

View File

@ -5,7 +5,7 @@
<IconCloseModal /> <IconCloseModal />
</q-btn> </q-btn>
<q-card-section class="modal-header"> <q-card-section class="modal-header" role="heading">
<h5 class="modal-header-title subtitle green-text"> <h5 class="modal-header-title subtitle green-text">
{{ modalTextContent[modalItem].title }} {{ modalTextContent[modalItem].title }}
</h5> </h5>
@ -195,6 +195,7 @@ export default defineComponent({
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
position: relative; position: relative;
gap: 60px;
&::after { &::after {
content: ''; content: '';
position: absolute; position: absolute;
@ -207,6 +208,10 @@ export default defineComponent({
transform: translateX(-50%); transform: translateX(-50%);
} }
& .custom-input-el {
flex: 1 0 100px;
}
@media only screen and (max-width: $med-md) { @media only screen and (max-width: $med-md) {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

View File

@ -0,0 +1,247 @@
<template>
<form class="question-form-body" @submit.prevent="onSubmit">
<div class="fields">
<q-input
v-model="firstName"
v-bind="firstNameAttrs"
:error-message="errors.fist_name"
:error="!!errors.fist_name"
bg-color="white"
label="Nombre"
class="name"
standout
/>
<q-input
v-model="secondName"
v-bind="secondNameAttrs"
:error-message="errors.second_name"
:error="!!errors.second_name"
bg-color="white"
label="Apellidos"
class="nickname"
standout
/>
<q-input
v-model="email"
v-bind="emailAttrs"
:error-message="errors.email"
:error="!!errors.email"
bg-color="white"
type="email"
label="Email"
class="email"
standout
/>
<q-input
v-model="telephone"
v-bind="telephoneAttrs"
:error-message="errors.telephone"
:error="!!errors.telephone"
bg-color="white"
type="tel"
label="Teléfono"
class="telephone"
mask="(##) ##### ####"
standout
/>
<q-input
v-model="query"
v-bind="queryAttrs"
:error-message="errors.query"
:error="!!errors.query"
bg-color="white"
label="Motivo de consulta"
class="consult"
standout
/>
<q-input
v-model="message"
v-bind="messageAttrs"
:error-message="errors.message"
:error="!!errors.message"
bg-color="white"
label="Mensaje"
class="message"
type="textarea"
autogrow
standout
/>
<q-checkbox
v-model="terms"
v-bind="termsAttrs"
:error-message="errors.terms"
:error="!!errors.terms"
class="terms"
>
*Acepto los términos y condiciones
<RouterLink class="terms-link" to="/">(Ver)</RouterLink>, y recibir
respuesta comercial por parte de Verdnatura
</q-checkbox>
</div>
<q-btn
type="submit"
class="question-submit-btn btn rounded"
flat
:disable="!terms"
>
Enviar solicitud <IconArrowRightOne />
</q-btn>
</form>
</template>
<script lang="ts">
import { toTypedSchema } from '@vee-validate/zod';
import { useFormStore } from 'src/stores/forms';
import { useForm } from 'vee-validate';
import { defineComponent } from 'vue';
import { z } from 'zod';
import { useQuasar } from 'quasar';
import IconArrowRightOne from 'src/components/icons/IconArrowRightOne.vue';
export default defineComponent({
name: 'QuestionForm',
components: { IconArrowRightOne },
setup() {
const $q = useQuasar();
const formStore = useFormStore();
const { handleQuestionData } = formStore;
const nameMessage = 'Sólo se aceptan una palabra y caracteres no numéricos';
const requiredMessage = 'Campo obligatorio';
const validationSchema = toTypedSchema(
z
.object({
fist_name: z
.string({ required_error: requiredMessage })
.regex(/^[A-Za-z]+$/, nameMessage),
second_name: z
.string({ required_error: requiredMessage })
.regex(/^[A-Za-z]+$/, nameMessage),
email: z.string({ required_error: requiredMessage }).email(),
telephone: z.string({ required_error: requiredMessage }),
query: z.string({ required_error: requiredMessage }),
message: z.string({ required_error: requiredMessage }),
terms: z.boolean({ required_error: requiredMessage }),
})
.required({})
);
const { errors, meta, defineField, handleSubmit, handleReset } = useForm({
validationSchema,
});
const [firstName, firstNameAttrs] = defineField('fist_name', {});
const [secondName, secondNameAttrs] = defineField('second_name', {});
const [email, emailAttrs] = defineField('email', {});
const [telephone, telephoneAttrs] = defineField('telephone', {});
const [query, queryAttrs] = defineField('query', {});
const [message, messageAttrs] = defineField('message', {});
const [terms, termsAttrs] = defineField('terms', {});
const onSubmit = handleSubmit((values) => {
console.log(values);
handleQuestionData(values);
handleReset();
if (!terms.value) {
$q.notify({
color: 'negative',
message: 'Primero tienes que aceptar la licencia y las condiciones',
});
return;
}
$q.notify({
icon: 'done',
color: 'positive',
message: 'Enviado',
});
});
return {
errors,
firstName,
firstNameAttrs,
secondName,
secondNameAttrs,
email,
emailAttrs,
telephone,
telephoneAttrs,
query,
queryAttrs,
message,
messageAttrs,
terms,
termsAttrs,
onSubmit,
meta,
};
},
});
</script>
<style lang="scss" scoped>
.question-form-body {
display: flex;
flex-direction: column;
gap: 24px;
& label {
padding-bottom: initial;
}
& .fields {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
& .q-field {
caret-color: $primary;
font-family: $font-questrial;
font-weight: 400;
line-height: 22px;
font-size: $font-14;
&.q-field--error {
transition: 200ms ease-in-out;
margin-bottom: 30px;
color: #fff;
& div[role='alert'] {
color: #fff !important;
}
}
&.name,
&.nickname,
&.email,
&.telephone {
flex: 1 0 min(100%, 218px);
}
&.consult,
&.message {
flex: 1 0 100%;
}
}
& .question-submit-btn {
align-self: flex-start;
}
}
.q-checkbox {
&.terms {
color: $white;
font-family: $font-questrial;
font-size: $font-14;
line-height: 18px;
letter-spacing: 0.28px;
& .terms-link {
color: #f90;
&:hover {
text-decoration-line: underline;
}
}
}
}
</style>

View File

@ -5,7 +5,7 @@
</q-no-ssr> </q-no-ssr>
<mobile-nav /> <mobile-nav />
<q-page-container class="no-padding padding-top more" role="main"> <q-page-container class="no-padding padding-top more">
<router-view /> <router-view />
</q-page-container> </q-page-container>

View File

@ -5,10 +5,12 @@
</q-no-ssr> </q-no-ssr>
<mobile-nav /> <mobile-nav />
<q-page-container class="no-padding padding-top more" role="main"> <q-page-container class="no-padding padding-top more">
<router-view /> <router-view />
</q-page-container> </q-page-container>
<dudas-section is-white />
<question-section /> <question-section />
<footer-component /> <footer-component />
@ -21,13 +23,20 @@ import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import FooterComponent from 'src/components/footer/FooterComponent.vue'; import FooterComponent from 'src/components/footer/FooterComponent.vue';
import HeaderSecondary from 'src/components/header/HeaderSecondary.vue'; import HeaderSecondary from 'src/components/header/HeaderSecondary.vue';
import DudasSection from 'src/components/sections/DudasSection.vue';
import QuestionSection from 'src/components/sections/QuestionSection.vue'; import QuestionSection from 'src/components/sections/QuestionSection.vue';
import MobileNav from 'src/components/ui/MobileNav.vue'; import MobileNav from 'src/components/ui/MobileNav.vue';
import { useMobileStore } from 'src/stores/mobileNav'; import { useMobileStore } from 'src/stores/mobileNav';
export default defineComponent({ export default defineComponent({
name: 'ProductLayout', name: 'ProductLayout',
components: { HeaderSecondary, FooterComponent, QuestionSection, MobileNav }, components: {
HeaderSecondary,
FooterComponent,
QuestionSection,
MobileNav,
DudasSection,
},
setup() { setup() {
const mobileStore = useMobileStore(); const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore); const { isOpenNav } = storeToRefs(mobileStore);

View File

@ -5,7 +5,7 @@
</q-no-ssr> </q-no-ssr>
<mobile-nav /> <mobile-nav />
<q-page-container class="no-padding more product-layout" role="main"> <q-page-container class="no-padding more product-layout">
<router-view /> <router-view />
</q-page-container> </q-page-container>

View File

@ -5,7 +5,7 @@
</q-no-ssr> </q-no-ssr>
<mobile-nav /> <mobile-nav />
<q-page-container class="no-padding" role="main"> <q-page-container class="no-padding">
<router-view /> <router-view />
</q-page-container> </q-page-container>
@ -18,9 +18,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { storeToRefs } from 'pinia';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import FooterComponent from 'src/components/footer/FooterComponent.vue'; import FooterComponent from 'src/components/footer/FooterComponent.vue';
import HeaderPrimary from 'src/components/header/HeaderPrimary.vue'; import HeaderPrimary from 'src/components/header/HeaderPrimary.vue';
import InfoSection from 'src/components/sections/InfoSection.vue'; import InfoSection from 'src/components/sections/InfoSection.vue';

View File

@ -2,7 +2,7 @@
<q-page class="category-container"> <q-page class="category-container">
<section class="products-section"> <section class="products-section">
<header class="products-section-header"> <header class="products-section-header">
<container> <Container>
<div class="product-header-content"> <div class="product-header-content">
<h3 class="product-header-title subtitle">Ramos para obsequiar</h3> <h3 class="product-header-title subtitle">Ramos para obsequiar</h3>
<p class="product-header-paragraph"> <p class="product-header-paragraph">
@ -17,28 +17,36 @@
<div class="filter-item availability-filter"> <div class="filter-item availability-filter">
<p class="filter-paragraph availability"> <p class="filter-paragraph availability">
Disponibilidad para: Disponibilidad para:
<span class="green-text">25 Julio en 08005</span> <span
v-if="availability.date && availability.postalCode"
class="green-text"
>
25 Julio en
{{ availability.postalCode.replace('-', '') }}</span
>
</p> </p>
<button <q-btn
flat
class="btn filter-btn availability" class="btn filter-btn availability"
type="button" type="button"
@click="modalStore.openModal({ modal: 'availability' })" @click="modalStore.openModal({ modal: 'availability' })"
> >
<IconPencil /> <IconPencil />
</button> </q-btn>
</div> </div>
</div> </div>
<div class="product-right-filters"> <div class="product-right-filters">
<button <q-btn
flat
class="btn filter-item filters filter-btn" class="btn filter-item filters filter-btn"
type="button" type="button"
@click="modalStore.openModal({ modal: 'filters' })" @click="modalStore.openModal({ modal: 'filters' })"
> >
<p class="filter-paragraph remove-mob">Filtros</p> <p class="filter-paragraph remove-mob">Filtros</p>
<IconFilter /> <IconFilter />
</button> </q-btn>
<div class="filter-item order-filter"> <div class="filter-item order-filter">
<p class="filter-paragraph"> <p class="filter-paragraph">
@ -46,30 +54,30 @@
<span class="green-text">precio</span> <span class="green-text">precio</span>
</p> </p>
<button type="button" class="btn filter-btn price-order"> <q-btn flat type="button" class="btn filter-btn price-order">
<IconArrowDownWhite /> <IconArrowDownWhite />
</button> </q-btn>
</div> </div>
</div> </div>
</div> </div>
</container> </Container>
</header> </header>
<div class="products-section-body"> <div class="products-section-body">
<container cardContainer> <Container cardContainer>
<card <Card
v-for="( v-for="(
{ imgSrc, discount, isNew, title, value, id }, i { imgSrc, discount, isNew, title, value, id }, i
) in cardMock" ) in cardsMock"
:productValue="value" :productValue="value"
:productName="title" :productName="title"
:discount="discount" :discount="discount.toString()"
:imgSrc="imgSrc" :imgSrc="imgSrc"
:isNew="isNew" :isNew="isNew"
:key="i" :key="i"
:id="id" :id="id"
/> />
</container> </Container>
</div> </div>
<footer class="products-section-footer"> <footer class="products-section-footer">
@ -86,6 +94,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { fakerES } from '@faker-js/faker';
import { storeToRefs } from 'pinia';
import { defineComponent, ref } from 'vue';
import { useRoute } from 'vue-router';
import IconArrowCircleFilledRight from 'src/components/icons/IconArrowCircleFilledRight.vue'; import IconArrowCircleFilledRight from 'src/components/icons/IconArrowCircleFilledRight.vue';
import IconArrowDownWhite from 'src/components/icons/IconArrowDownWhite.vue'; import IconArrowDownWhite from 'src/components/icons/IconArrowDownWhite.vue';
import IconFilter from 'src/components/icons/IconFilter.vue'; import IconFilter from 'src/components/icons/IconFilter.vue';
@ -94,10 +107,22 @@ import DudasSection from 'src/components/sections/DudasSection.vue';
import Card from 'src/components/ui/Card.vue'; import Card from 'src/components/ui/Card.vue';
import Container from 'src/components/ui/Container.vue'; import Container from 'src/components/ui/Container.vue';
import Modal from 'src/components/ui/Modal.vue'; import Modal from 'src/components/ui/Modal.vue';
import { cardMock } from 'src/mock/cards'; import { useFormStore } from 'src/stores/forms';
import { useModalStore } from 'src/stores/modalStore'; import { useModalStore } from 'src/stores/modalStore';
import { defineComponent } from 'vue'; type MonthES =
| 'Enero'
| 'Febrero'
| 'Marzo'
| 'Abril'
| 'Mayo'
| 'Junio'
| 'Julio'
| 'Agosto'
| 'Septiembre'
| 'Octubre'
| 'Noviembre'
| 'Diciembre';
export default defineComponent({ export default defineComponent({
name: 'CategoryPage', name: 'CategoryPage',
@ -113,8 +138,38 @@ export default defineComponent({
}, },
setup() { setup() {
const modalStore = useModalStore(); const modalStore = useModalStore();
const formStore = useFormStore();
const { availability } = storeToRefs(formStore);
const monthES: Record<number, MonthES> = {
0: 'Enero',
1: 'Febrero',
2: 'Marzo',
3: 'Abril',
4: 'Mayo',
5: 'Junio',
6: 'Julio',
7: 'Agosto',
8: 'Septiembre',
9: 'Octubre',
10: 'Noviembre',
11: 'Diciembre',
};
// monthES[] || console.error('Invalid date');
return { cardMock, modalStore }; const category = ref('');
const { path } = useRoute();
const [_a, _b, categoryValue] = path.split(/\//);
const cardsMock = Array.from({ length: 8 }, (_, i) => ({
id: i + 1,
imgSrc: `../assets/flowers/flower-${i + 1}.png`,
discount: fakerES.number.int({ min: 5, max: 15 }),
isNew: fakerES.datatype.boolean(),
title: fakerES.commerce.product(),
value: fakerES.commerce.price({ min: 30, max: 100 }),
}));
return { modalStore, availability, cardsMock, category };
}, },
}); });
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<q-page class="checkout-page"> <q-page class="checkout-page">
<div class="container"> <Container tag="section">
<header class="header-title"> <header class="header-title">
<h1 class="pege-title">¿A quién y dónde lo entregamos?</h1> <h1 class="pege-title">¿A quién y dónde lo entregamos?</h1>
@ -49,56 +49,73 @@
<div class="checkout-content"> <div class="checkout-content">
<div class="checkout-form-container"> <div class="checkout-form-container">
<div class="checkout-header-form"> <header class="checkout-header-form">
<h3>Instrucciones para la entrega</h3> <h3>Instrucciones para la entrega</h3>
</div> </header>
<div class="checkout-form-wrapper">
<div class="checkout-form"> <div class="checkout-form">
<q-form <q-form
action="" action=""
method="post" method="post"
@submit.prevent="(e) => onSubmitPersonalData(e)" @submit.prevent="onSubmitPersonalData"
> >
<div class="form-fields-container"> <div class="form-fields-container delivery">
<div class="field-control field-input"> <div class="field-control field-input">
<q-input <q-input
placeholder="Nombre*" placeholder="Nombre*"
name="name" name="name"
type="text" type="text"
v-model="formPersonalData.data.name" v-model="formPersonalData.data.name"
:rules="[(val) => val.length > 0 || 'Campo obligatorio']" :rules="[
></q-input> (val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div> </div>
<div class="field-control field-input"> <div class="field-control field-input">
<q-input <q-input
placeholder="Apellidos*" placeholder="Apellidos*"
name="surname" name="surname"
type="text" type="text"
v-model="formPersonalData.data.surname" v-model="formPersonalData.data.surname"
:rules="[(val) => val.length > 0 || 'Campo obligatorio']" :rules="[
></q-input> (val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div> </div>
<div class="field-control field-input"> <div class="field-control field-input">
<q-input <q-input
placeholder="Dirección*" placeholder="Dirección*"
name="address" name="address"
type="text" type="text"
v-model="formPersonalData.data.address" v-model="formPersonalData.data.address"
:rules="[(val) => val.length > 0 || 'Campo obligatorio']" :rules="[
></q-input> (val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div> </div>
<div class="field-control field-input"> <div class="field-control field-input">
<q-input <q-input
placeholder="Código postal*" placeholder="Código postal*"
name="postalCode" name="postalCode"
type="text" type="text"
v-model="formPersonalData.data.postalCode" v-model="formPersonalData.data.postalCode"
:rules="[(val) => val.length > 0 || 'Campo obligatorio']" :rules="[
></q-input> (val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div> </div>
<div class="field-control field-select"> <div class="field-control field-select">
<q-select <q-select
filled outlined
v-model="formPersonalData.data.city" v-model="formPersonalData.data.city"
:options="optionsCity.data" :options="optionsCity.data"
:label=" :label="
@ -107,11 +124,12 @@
: 'Ciudad*' : 'Ciudad*'
" "
stack-label stack-label
></q-select> />
</div> </div>
<div class="field-control field-select"> <div class="field-control field-select">
<q-select <q-select
filled outlined
v-model="formPersonalData.data.province" v-model="formPersonalData.data.province"
:options="optionsProvince.data" :options="optionsProvince.data"
:label=" :label="
@ -120,33 +138,168 @@
: 'Provincia*' : 'Provincia*'
" "
stack-label stack-label
></q-select> />
</div> </div>
<div class="field-control field-input"> <div class="field-control field-input">
<q-input <q-input
outlined
placeholder="Teléfono*" placeholder="Teléfono*"
name="phone" name="phone"
type="text" type="text"
v-model="formPersonalData.data.phone" v-model="formPersonalData.data.phone"
:rules="[(val) => val.length > 0 || 'Campo obligatorio']" :rules="[
></q-input> (val) => val.length > 0 || 'Campo obligatorio',
]"
/>
</div> </div>
</div> </div>
<div class="form-fields-container sender">
<div class="field-control field-input">
<q-input
placeholder="Nombre*"
name="name"
type="text"
v-model="formPersonalData.data.name"
:rules="[
(val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div>
<div class="field-control field-input">
<q-input
placeholder="Nombre*"
name="name"
type="text"
v-model="formPersonalData.data.name"
:rules="[
(val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div>
<div class="field-control field-input">
<q-input
placeholder="Nombre*"
name="name"
type="text"
v-model="formPersonalData.data.name"
:rules="[
(val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div>
<div class="field-control field-input">
<q-input
placeholder="Nombre*"
name="name"
type="text"
v-model="formPersonalData.data.name"
:rules="[
(val) => val.length > 0 || 'Campo obligatorio',
]"
outlined
/>
</div>
<div class="field-control field-input">
<q-input
v-model="formPersonalData.data.name"
:rules="[
(val) => val.length > 0 || 'Campo obligatorio',
]"
bg-color="white"
label="Mensaje"
class="message"
type="textarea"
autogrow
outlined
/>
</div>
</div>
<div class="form-button-control"> <div class="form-button-control">
<q-btn color="primary" type="submit">Continuar</q-btn> <q-btn color="primary" type="submit">Continuar</q-btn>
</div> </div>
</q-form> </q-form>
</div> </div>
<aside class="checkout-aside">
<div class="checkout-delivery-date" v-if="true">
<header class="checkout-aside-header">
<strong> Fecha de entrega </strong>
</header>
<div class="checkout-delivery-body">
<p>13 de julio - De 11h - 12 h</p>
</div> </div>
<div class="checkout-inform"></div> </div>
<div class="checkout-summary">
<header class="checkout-aside-header">
<strong> Resumen del pedido </strong>
</header>
<div class="checkout-summary-body">
<ul class="checkout-summary-list">
<li class="checkout-summary-item">
<p>Ramo Lucena <span>30,00</span></p>
</li>
<li class="checkout-summary-item">
<p>Ramo Lucena <span>30,00</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-price">67</p>
</footer>
</div>
<div class="checkout-payment-methods">
<header class="checkout-aside-header">
<strong>Método de pago</strong>
</header>
<div class="checkout-payment-body">
<q-radio v-model="priceMethod" val="credit" color="primary">
<p>a</p>
</q-radio>
<q-radio v-model="priceMethod" val="stripe" color="primary">
<p>b</p>
</q-radio>
</div>
</div>
<div class="checkout-terms">
<q-checkbox v-model="terms" class="terms">
He leído y estoy de acuerdo con los términosy condiciones de
la tienda Floranet
</q-checkbox>
<q-btn flat class="btn" type="submit">CONTINUAR</q-btn>
</div>
</aside>
</div> </div>
</div> </div>
</div> </div>
</div>
</Container>
</q-page> </q-page>
</template> </template>
<script lang="ts" setup> <script lang="ts">
import { reactive, watch } from 'vue'; import Container from 'src/components/ui/Container.vue';
import { defineComponent, reactive, ref, watch } from 'vue';
interface StepsProps { interface StepsProps {
value: number; value: number;
@ -165,10 +318,23 @@ interface FormPersonalData {
province: string; province: string;
} }
//Steps control export default defineComponent({
const stepActive = reactive({ data: 1 }); name: 'CheckoutPage',
components: { Container },
const stepList = reactive<{ data: StepsProps[] }>({ setup() {
const formPersonalData = reactive<{ data: FormPersonalData }>({
data: {
name: '',
surname: '',
address: '',
postalCode: '',
phone: '',
city: '',
province: '',
},
});
const stepActive = reactive({ data: 1 });
const stepList = reactive<{ data: StepsProps[] }>({
data: [ data: [
{ {
value: 1, value: 1,
@ -189,42 +355,30 @@ const stepList = reactive<{ data: StepsProps[] }>({
active: false, active: false,
}, },
], ],
}); });
const handleClickStep = (value: number) => {
const handleClickStep = (value: number) => {
stepActive['data'] = value; stepActive['data'] = value;
}; };
const stepsFormated = () => {
const stepsFormated = () => {
return stepList['data'].map((step) => { return stepList['data'].map((step) => {
if (step.value === stepActive['data']) { if (step.value === stepActive['data']) {
return { ...step, active: true }; return { ...step, active: true };
} }
return step; return step;
}); });
}; };
//Form personal const optionsCity = reactive<{ data: string[] }>({
const formPersonalData = reactive<{ data: FormPersonalData }>({
data: {
name: '',
surname: '',
address: '',
postalCode: '',
phone: '',
city: '',
province: '',
},
});
const optionsCity = reactive<{ data: string[] }>({
data: ['Complete la dirección y el código postal'], data: ['Complete la dirección y el código postal'],
}); });
const optionsProvince = reactive<{ data: string[] }>({ const optionsProvince = reactive<{ data: string[] }>({
data: ['Complete la dirección, código postal y seleccione la ciudad'], data: ['Complete la dirección, código postal y seleccione la ciudad'],
}); });
watch( watch(
() => formPersonalData.data.postalCode, () => formPersonalData.data.postalCode,
() => { () => {
if ( if (
@ -242,9 +396,9 @@ watch(
optionsCity.data = ['Complete la dirección y el código postal']; optionsCity.data = ['Complete la dirección y el código postal'];
formPersonalData.data.city = ''; formPersonalData.data.city = '';
} }
); );
watch( watch(
() => formPersonalData.data.city, () => formPersonalData.data.city,
() => { () => {
if ( if (
@ -265,12 +419,28 @@ watch(
]; ];
formPersonalData.data.postalCode = ''; formPersonalData.data.postalCode = '';
} }
); );
function onSubmitPersonalData(e: Event) { function onSubmitPersonalData(_e: Event) {
e.preventDefault();
console.log(formPersonalData.data); console.log(formPersonalData.data);
} }
const priceMethod = ref('credit');
const terms = ref(false);
return {
handleClickStep,
stepsFormated,
onSubmitPersonalData,
formPersonalData,
optionsProvince,
optionsCity,
stepList,
priceMethod,
terms,
};
},
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -347,6 +517,19 @@ function onSubmitPersonalData(e: Event) {
line-height: 1.5; line-height: 1.5;
} }
} }
& .checkout-form-wrapper {
display: flex;
flex-wrap: wrap;
gap: 20px;
& .checkout-form {
flex: 1 0 min(100%, 795px);
}
& .checkout-aside {
flex: 1 0 min(100%, 329px);
}
}
} }
.form-fields-container { .form-fields-container {
display: flex; display: flex;
@ -354,6 +537,7 @@ function onSubmitPersonalData(e: Event) {
gap: 10px 15px; gap: 10px 15px;
.field-control { .field-control {
flex-basis: calc(50% - 8px); flex-basis: calc(50% - 8px);
&.field-input { &.field-input {
label { label {
padding-bottom: 10px; padding-bottom: 10px;

View File

@ -4,10 +4,10 @@
<VerticalCarouselImgs :imgsArr="slidesContent" class="home-carousel" /> <VerticalCarouselImgs :imgsArr="slidesContent" class="home-carousel" />
</q-no-ssr> </q-no-ssr>
<!-- <p v-if="mobileStore.isCarouselVisible">Está visível</p> <!-- <p v-if="isCarouselVisible">Está visível</p>
<p v-if="!mobileStore.isCarouselVisible">Não está visível</p> <p v-if="!isCarouselVisible">Não está visível</p>
<p v-if="mobileStore.isOpenNav">Hamburg ativo</p> <p v-if="isOpenNav">Hamburg ativo</p>
<p v-if="!mobileStore.isOpenNav">Hamburg não ativo</p> --> <p v-if="!isOpenNav">Hamburg não ativo</p> -->
<section class="products-section"> <section class="products-section">
<header class="products-section-header section-header"> <header class="products-section-header section-header">
@ -71,11 +71,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineAsyncComponent, defineComponent, ref } from 'vue';
import { storeToRefs } from 'pinia';
import IconArrowCircleFilledRight from 'src/components/icons/IconArrowCircleFilledRight.vue'; import IconArrowCircleFilledRight from 'src/components/icons/IconArrowCircleFilledRight.vue';
import VerticalCarouselImgs from 'src/components/quasar-components/carousel/VerticalCarouselImgs.vue'; import VerticalCarouselImgs from 'src/components/quasar-components/carousel/VerticalCarouselImgs.vue';
import Card from 'src/components/ui/Card.vue';
import Container from 'src/components/ui/Container.vue'; import Container from 'src/components/ui/Container.vue';
import { cardMock } from 'src/mock/cards'; import { cardMock } from 'src/mock/cards';
import { useMobileStore } from 'src/stores/mobileNav'; import { useMobileStore } from 'src/stores/mobileNav';
@ -89,10 +89,15 @@ export default defineComponent({
// ButtonComponent, // ButtonComponent,
// SwiperSlide, // SwiperSlide,
Container, Container,
Card, Card: defineAsyncComponent({
loader: () => import('src/components/ui/Card.vue'),
loadingComponent: { template: '<p>loading</p>' },
}),
}, },
setup() { setup() {
const mobileStore = useMobileStore(); const mobileStore = useMobileStore();
const { isCarouselVisible, isOpenNav, screenWidth } =
storeToRefs(mobileStore);
const slidesContent = [ const slidesContent = [
'assets/1.jpg', 'assets/1.jpg',
'assets/2.jpg', 'assets/2.jpg',
@ -100,11 +105,15 @@ export default defineComponent({
'assets/4.jpg', 'assets/4.jpg',
'assets/5.jpg', 'assets/5.jpg',
]; ];
const data = ref(null);
return { return {
slidesContent, slidesContent,
cardMock, cardMock,
mobileStore, isCarouselVisible,
isOpenNav,
screenWidth,
data,
}; };
}, },
}); });

View File

@ -14,15 +14,20 @@ const routes: RouteRecordRaw[] = [
], ],
}, },
{ {
path: '/category', path: '/categoria',
component: () => import('layouts/CategoryLayout.vue'), component: () => import('layouts/CategoryLayout.vue'),
redirect: '/category', redirect: '/categoria',
children: [ children: [
{ {
path: '', path: 'ramos',
name: 'Category', name: 'Category',
component: () => import('pages/CategoryPage.vue'), component: () => import('pages/CategoryPage.vue'),
}, },
{
path: 'plantas',
name: 'Plantas',
component: () => import('pages/CategoryPage.vue'),
},
], ],
}, },
{ {

31
src/stores/forms.ts Normal file
View File

@ -0,0 +1,31 @@
import { defineStore } from 'pinia';
export const useFormStore = defineStore('forms', {
state: () => ({
question: {
fist_name: '',
second_name: '',
email: '',
telephone: '',
query: '',
message: '',
terms: false,
},
availability: {
date: '',
postalCode: '',
},
}),
actions: {
handleQuestionData(values: typeof this.question) {
console.log(values);
this.question = values;
},
handleAvailabilityData(values: typeof this.availability) {
console.log(values);
this.availability = values;
},
},
});

7
src/stores/language.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineStore } from 'pinia';
export const useLanguageStore = defineStore('language', {
state: () => ({
lang: 'es',
}),
});