cambios programación semana 22
This commit is contained in:
parent
5197f896d1
commit
3ca9560313
|
@ -17,6 +17,13 @@
|
||||||
"terminal.integrated.cursorStyleInactive": "line",
|
"terminal.integrated.cursorStyleInactive": "line",
|
||||||
"workbench.iconTheme": "material-icon-theme",
|
"workbench.iconTheme": "material-icon-theme",
|
||||||
"workbench.colorTheme": "Default Dark+",
|
"workbench.colorTheme": "Default Dark+",
|
||||||
|
"editor.codeActionsOnSave": [
|
||||||
|
"source.addMissingImports",
|
||||||
|
"source.organizeImports",
|
||||||
|
"source.fixAll.eslint",
|
||||||
|
"source.fixAll.stylelint"
|
||||||
|
],
|
||||||
|
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"**/.git": true,
|
"**/.git": true,
|
||||||
"**/.svn": true,
|
"**/.svn": true,
|
||||||
|
@ -46,7 +53,6 @@
|
||||||
"git.confirmSync": false,
|
"git.confirmSync": false,
|
||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
"console-ninja.featureSet": "Community",
|
"console-ninja.featureSet": "Community",
|
||||||
"liveServer.settings.donotShowInfoMsg": true,
|
|
||||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
"editor.cursorSmoothCaretAnimation": "on",
|
"editor.cursorSmoothCaretAnimation": "on",
|
||||||
"editor.fontLigatures": true,
|
"editor.fontLigatures": true,
|
||||||
|
@ -60,9 +66,7 @@
|
||||||
"source.organizeImports",
|
"source.organizeImports",
|
||||||
"source.fixAll.eslint"
|
"source.fixAll.eslint"
|
||||||
],
|
],
|
||||||
"liveServer.settings.donotVerifyTags": true,
|
|
||||||
"gitlens.gitCommands.skipConfirmations": ["fetch:command", "switch:command"],
|
"gitlens.gitCommands.skipConfirmations": ["fetch:command", "switch:command"],
|
||||||
"symbols.hidesExplorerArrows": false,
|
|
||||||
"diffEditor.ignoreTrimWhitespace": false,
|
"diffEditor.ignoreTrimWhitespace": false,
|
||||||
"svg.preview.mode": "svg",
|
"svg.preview.mode": "svg",
|
||||||
"[svg]": {
|
"[svg]": {
|
||||||
|
@ -72,7 +76,7 @@
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"workbench.tree.indent": 16,
|
"workbench.tree.indent": 16,
|
||||||
"window.zoomLevel": 0,
|
"window.zoomLevel": -1,
|
||||||
"git.ignoreRebaseWarning": true,
|
"git.ignoreRebaseWarning": true,
|
||||||
"editor.largeFileOptimizations": false,
|
"editor.largeFileOptimizations": false,
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
|
|
|
@ -5,45 +5,34 @@ const productsJson = require("./products.json")
|
||||||
class ProductController {
|
class ProductController {
|
||||||
async findAll(req, res) {
|
async findAll(req, res) {
|
||||||
|
|
||||||
|
|
||||||
const _products = await db.getProducts();
|
|
||||||
const mapedProducts = await _products[0].map(function (item) {
|
|
||||||
return {
|
|
||||||
id: item.id,
|
|
||||||
name: item.name,
|
|
||||||
description: item.description,
|
|
||||||
type: item.type,
|
|
||||||
price: item.price,
|
|
||||||
specialPrice: item.specialPrice,
|
|
||||||
isNew: item.isNew,
|
|
||||||
slug: item.slug,
|
|
||||||
category: item.category,
|
|
||||||
postalCode: item.postalCode,
|
|
||||||
dateExpired: item.dateExpired,
|
|
||||||
images: [item.image],
|
|
||||||
featured: item.featured,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//Gerar produtos fake
|
|
||||||
/* const fakeProducts = generateFlowers(50)
|
|
||||||
console.log(fakeProducts); */
|
|
||||||
const params = req.query;
|
const params = req.query;
|
||||||
let productsFilter = mapedProducts
|
const _products = await db.getProducts(params.dateExpired, params.postalCode);
|
||||||
console.log(params);
|
let productsFilter = _products[0];
|
||||||
|
|
||||||
if (Number(params.featured)) {
|
if (Number(params.recommend)) {
|
||||||
productsFilter = productsFilter.filter(item => item.featured === Number(params.featured))
|
productsFilter = productsFilter.filter(item => item.recommend == Number(params.recommend))
|
||||||
}
|
}
|
||||||
if (params.category) {
|
if (params.type) {
|
||||||
productsFilter = productsFilter.filter(item => item.category === Number(params.category))
|
productsFilter = productsFilter.filter(item => item.type === params.type)
|
||||||
}
|
}
|
||||||
if (params.postalCode) {
|
/*if (params.postalCode) {
|
||||||
productsFilter = productsFilter.filter(item => item.postalCode === params.postalCode)
|
productsFilter = productsFilter.filter(item => item.postalCode === params.postalCode)
|
||||||
}
|
}
|
||||||
|
if (params.dateExpired) {
|
||||||
|
const dateSearch = new Date(params.dateExpired);
|
||||||
|
productsFilter = productsFilter.filter(item => {
|
||||||
|
const dateProduct = new Date(item.dateExpired);
|
||||||
|
if (dateProduct >= dateSearch) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
console.log(productsFilter.length);
|
||||||
|
|
||||||
|
|
||||||
if (params.minPrice && !params.maxPrice) {
|
if (params.minPrice && !params.maxPrice) {
|
||||||
productsFilter = productsFilter.filter(item => {
|
productsFilter = productsFilter.filter(item => {
|
||||||
const price = Number(item.price.replace(/€/g, ''))
|
const price = Number(item.price)
|
||||||
if (price >= Number(params.minPrice)) {
|
if (price >= Number(params.minPrice)) {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
@ -51,7 +40,7 @@ class ProductController {
|
||||||
}
|
}
|
||||||
if (params.maxPrice && !params.minPrice) {
|
if (params.maxPrice && !params.minPrice) {
|
||||||
productsFilter = productsFilter.filter(item => {
|
productsFilter = productsFilter.filter(item => {
|
||||||
const price = Number(item.price.replace(/€/g, ''))
|
const price = Number(item.price)
|
||||||
if (price <= Number(params.maxPrice)) {
|
if (price <= Number(params.maxPrice)) {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
@ -59,69 +48,75 @@ class ProductController {
|
||||||
}
|
}
|
||||||
if (params.maxPrice && params.minPrice) {
|
if (params.maxPrice && params.minPrice) {
|
||||||
productsFilter = productsFilter.filter(item => {
|
productsFilter = productsFilter.filter(item => {
|
||||||
const price = Number(item.price.replace(/€/g, ''))
|
const price = Number(item.price)
|
||||||
if (price >= Number(params.minPrice) && price <= Number(params.maxPrice)) {
|
if (price >= Number(params.minPrice) && price <= Number(params.maxPrice)) {
|
||||||
console.log(price);
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (params.dateExpired) {
|
|
||||||
const [day, month, year] = params.dateExpired.split("/");
|
|
||||||
const dateSearch = new Date(year, month - 1, day);
|
|
||||||
productsFilter = productsFilter.filter(item => {
|
|
||||||
const [day, month, year] = item.dateExpired.split("/");
|
|
||||||
const dateProduct = new Date(year, month - 1, day);
|
|
||||||
if (dateProduct >= dateSearch) {
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Number(params.bigPrice)) {
|
if (Number(params.bigPrice)) {
|
||||||
productsFilter.sort((a, b) => {
|
productsFilter.sort((a, b) => {
|
||||||
const itemA = Number(a.price.replace(/€/g, ''))
|
const itemA = Number(a.price)
|
||||||
const itemB = Number(b.price.replace(/€/g, ''))
|
const itemB = Number(b.price)
|
||||||
return itemB - itemA;
|
return itemB - itemA;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(params.lowPrice)) {
|
if (Number(params.lowPrice)) {
|
||||||
productsFilter.sort((a, b) => {
|
productsFilter.sort((a, b) => {
|
||||||
const itemA = Number(a.price.replace(/€/g, ''))
|
const itemA = Number(a.price)
|
||||||
const itemB = Number(b.price.replace(/€/g, ''))
|
const itemB = Number(b.price)
|
||||||
return itemA - itemB;
|
return itemA - itemB;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.isNew) {
|
if (Number(params.order_descending)) {
|
||||||
productsFilter = productsFilter.filter(item => item.isNew === true)
|
productsFilter.sort((a, b) => {
|
||||||
|
const itemA = a.order_position
|
||||||
|
const itemB = b.order_position
|
||||||
|
return itemB - itemA;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let productsFilterPages = []
|
if (Number(params.order_crescent)) {
|
||||||
const totalItens = params?.itens ? Number(params.itens) : 200
|
productsFilter.sort((a, b) => {
|
||||||
const page = params.page ? Number(params.page) : 1
|
const itemA = a.order_position
|
||||||
const startIndex = (totalItens * page) - totalItens
|
const itemB = b.order_position
|
||||||
const lastIndex = (totalItens * page)
|
return itemA - itemB;
|
||||||
const products = productsFilter.slice(startIndex, lastIndex)
|
})
|
||||||
productsFilterPages.push({
|
}
|
||||||
page: page,
|
|
||||||
productsPerPage: products.length,
|
if (Number(params.isNew)) {
|
||||||
products: products
|
console.log(params.isNew);
|
||||||
})
|
productsFilter = productsFilter.filter(item => item.isNew == Number(params.isNew))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* let productsFilterPages = []
|
||||||
|
const totalItens = params?.itens ? Number(params.itens) : 200
|
||||||
|
const page = params.page ? Number(params.page) : 1
|
||||||
|
const startIndex = (totalItens * page) - totalItens
|
||||||
|
const lastIndex = (totalItens * page)
|
||||||
|
const products = productsFilter.slice(startIndex, lastIndex)
|
||||||
|
productsFilterPages.push({
|
||||||
|
page: page,
|
||||||
|
productsPerPage: products.length,
|
||||||
|
products: products
|
||||||
|
}) */
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
data: productsFilterPages
|
data: productsFilter
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
findBySlug(req, res) {
|
async findById(req, res) {
|
||||||
const slug = req.params.slug
|
const id = Number(req.params.id)
|
||||||
const products = productsJson
|
const _products = await db.getProducts();
|
||||||
const filterSlug = products.filter(item => item.slug === slug)
|
const filterProduct = _products[0].filter(item => item.id === id)
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
data: filterSlug
|
data: filterProduct
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
api/db/db.js
34
api/db/db.js
|
@ -1,27 +1,27 @@
|
||||||
async function connect() {
|
async function connect() {
|
||||||
if (global.connection && global.connection.state !== 'disconnected')
|
if (global.connection && global.connection.state !== 'disconnected')
|
||||||
return global.connection;
|
return global.connection;
|
||||||
|
|
||||||
const host = process.env.HOST;
|
const host = process.env.HOST;
|
||||||
const port = process.env.PORT;
|
const port = process.env.PORT;
|
||||||
const database = process.env.DATABASE;
|
const database = process.env.DATABASE;
|
||||||
const user = process.env.DB_USER;
|
const user = process.env.DB_USER;
|
||||||
const password = process.env.DB_PASSWORD;
|
const password = process.env.DB_PASSWORD;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mysql = require("mysql2/promise");
|
const mysql = require("mysql2/promise");
|
||||||
const connection = await mysql.createConnection("mysql://" + user + ":" + password + "@" + host + ":" + port + "/" + database + "");
|
const connection = await mysql.createConnection("mysql://" + user + ":" + password + "@" + host + ":" + port + "/" + database + "");
|
||||||
console.log("Connected to MySQL!");
|
console.log("Connected to MySQL!");
|
||||||
global.connection = connection;
|
global.connection = connection;
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getProducts() {
|
async function getProducts(dateExpired, postalCode) {
|
||||||
const conn = await connect();
|
console.log("Query in table MySQL!");
|
||||||
const [rows] = await conn.query('CALL catalogue_get("2024-01-30", "08001")');
|
const conn = await connect();
|
||||||
|
const [rows] = await conn.query(`CALL catalogue_get("${dateExpired}", "${postalCode}")`);
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ app.get('/', (req, res) => {
|
||||||
|
|
||||||
//Products
|
//Products
|
||||||
app.get('/api/products', productController.findAll);
|
app.get('/api/products', productController.findAll);
|
||||||
app.get('/api/products/slug/:slug', productController.findBySlug);
|
app.get('/api/products/:id', productController.findById);
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Server listening at http://localhost:${port}`);
|
console.log(`Server listening at http://localhost:${port}`);
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-element-bundle.min.js"
|
src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-element-bundle.min.js"
|
||||||
defer
|
defer
|
||||||
></script>
|
></script>
|
||||||
<script src="https://js.stripe.com/v3" defer></script>
|
<!-- <script src="https://js.stripe.com/v3" defer></script> -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- quasar:entry-point -->
|
<!-- quasar:entry-point -->
|
||||||
|
|
|
@ -28,7 +28,7 @@ module.exports = configure(function (/* ctx */) {
|
||||||
// app boot file (/src/boot)
|
// app boot file (/src/boot)
|
||||||
// --> boot files are part of "main.js"
|
// --> boot files are part of "main.js"
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
boot: ["i18n", "axios" /* , "stripe" */],
|
boot: ["i18n", "axios" /* , { path: "stripe", server: false } */],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
css: ["app.scss"],
|
css: ["app.scss"],
|
||||||
|
@ -119,7 +119,7 @@ module.exports = configure(function (/* ctx */) {
|
||||||
// directives: [],
|
// directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: ["Meta", "Loading", "Notify"],
|
plugins: ["Meta", "Loading", "Notify", "LocalStorage", "SessionStorage"],
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
|
|
|
@ -3,15 +3,15 @@ import { boot } from "quasar/wrappers";
|
||||||
|
|
||||||
// "async" is optional;
|
// "async" is optional;
|
||||||
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
|
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
|
||||||
export default boot(async ({ app, router, store }) => {
|
export default boot(({ app }) => {
|
||||||
if (typeof window === "undefined") return;
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
pk: process.env.STRIPE_PUBLISHABLE_KEY,
|
pk: "pk_test_51OZaJdIK1lTlG93d2y0B81n4XrjvjQwqfIUZ7ggb9wEBa1e4h34GlYFYPwjtGl3OUT7DJZlVNX9EMXaCdOBkIC3T007mLnfvCu",
|
||||||
stripeAccount: process.env.STRIPE_ACCOUNT,
|
stripeAccount: "acct_1OXQt7GMODwoSxWA",
|
||||||
apiVersion: process.env.API_VERSION,
|
apiVersion: "2023-10-16",
|
||||||
locale: process.env.LOCALE,
|
locale: "pt-BR",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
app.config.globalProperties.$stripe = StripePlugin.install = options;
|
||||||
|
|
||||||
app.use(StripePlugin, options);
|
app.use(StripePlugin, options);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,63 +1,45 @@
|
||||||
<script>
|
<script>
|
||||||
import { toTypedSchema } from "@vee-validate/zod";
|
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { quasarNotify } from "src/functions/quasarNotify";
|
import { fullCurrentDate } from "src/constants/date";
|
||||||
import { useFormStore } from "src/stores/forms";
|
import { useFormStore } from "src/stores/forms";
|
||||||
import { availabilitySchema } from "src/utils/zod/schemas/availabilitySchema";
|
import { defineComponent, ref } from "vue";
|
||||||
import { useForm } from "vee-validate";
|
|
||||||
import { defineComponent, ref, watch } from "vue";
|
|
||||||
import IconCalendar from "../icons/IconCalendar.vue";
|
import IconCalendar from "../icons/IconCalendar.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "calendar-input",
|
name: "calendar-input",
|
||||||
components: { IconCalendar },
|
components: { IconCalendar },
|
||||||
setup() {
|
inheritAttrs: true,
|
||||||
const getDate = new Date();
|
props: {
|
||||||
const currentDay = getDate.getDate().toString().padStart(2, "0");
|
setValues: {
|
||||||
const currentMonth = getDate.getMonth() + 1;
|
type: Function,
|
||||||
const currentYear = getDate.getFullYear();
|
default: () => {},
|
||||||
const fullCurrentDate = `${currentYear}/${currentMonth}/${currentDay}`;
|
},
|
||||||
|
},
|
||||||
|
setup({ setValues }) {
|
||||||
const formStore = useFormStore();
|
const formStore = useFormStore();
|
||||||
const { availability } = storeToRefs(formStore);
|
const { availability } = storeToRefs(formStore);
|
||||||
|
|
||||||
const proxyDate = ref(fullCurrentDate);
|
const proxyDate = ref(fullCurrentDate);
|
||||||
|
|
||||||
const validationSchema = toTypedSchema(
|
function updateProxy() {
|
||||||
availabilitySchema.pick({ date: true })
|
proxyDate.value = fullCurrentDate;
|
||||||
);
|
}
|
||||||
const { errors, defineField, values } = useForm({
|
|
||||||
validationSchema,
|
|
||||||
});
|
|
||||||
const [calendar, calendarAttrs] = defineField("date");
|
|
||||||
|
|
||||||
const onBlur = () => {
|
function optionsValidDates(date) {
|
||||||
availability.value.date = calendar.value;
|
return date >= fullCurrentDate;
|
||||||
};
|
}
|
||||||
availability.value.date = values.date;
|
|
||||||
|
|
||||||
watch(errors, (newErrors) => {
|
function save() {
|
||||||
if (newErrors.date) {
|
availability.value.date = proxyDate.value;
|
||||||
quasarNotify({ message: newErrors.date, type: "erro" });
|
setValues({ date: proxyDate.value });
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
availability,
|
availability,
|
||||||
proxyDate,
|
proxyDate,
|
||||||
calendar,
|
updateProxy,
|
||||||
calendarAttrs,
|
optionsValidDates,
|
||||||
errors,
|
save,
|
||||||
onBlur,
|
|
||||||
updateProxy() {
|
|
||||||
proxyDate.value = availability.value.date;
|
|
||||||
},
|
|
||||||
optionsValidDates(date) {
|
|
||||||
return date >= fullCurrentDate /* && date <= '2019/02/15' */;
|
|
||||||
},
|
|
||||||
save() {
|
|
||||||
availability.value.date = proxyDate.value;
|
|
||||||
calendar.value = proxyDate.value;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -76,6 +58,7 @@ export default defineComponent({
|
||||||
>
|
>
|
||||||
<q-date
|
<q-date
|
||||||
v-model="proxyDate"
|
v-model="proxyDate"
|
||||||
|
v-bind="calendarAttrs"
|
||||||
:options="optionsValidDates"
|
:options="optionsValidDates"
|
||||||
mask="DD-MM-YYYY"
|
mask="DD-MM-YYYY"
|
||||||
>
|
>
|
||||||
|
@ -95,16 +78,7 @@ export default defineComponent({
|
||||||
|
|
||||||
<div class="custom-block-content">
|
<div class="custom-block-content">
|
||||||
<p class="custom-head-paragraph">¿Cuándo?</p>
|
<p class="custom-head-paragraph">¿Cuándo?</p>
|
||||||
<!-- <q-input
|
|
||||||
class="custom-date-input"
|
|
||||||
placeholder="Elige una fecha"
|
|
||||||
v-model="calendar"
|
|
||||||
mask="##/##/####"
|
|
||||||
:error="!!errors.date"
|
|
||||||
@blur="onBlur"
|
|
||||||
borderless
|
|
||||||
dense
|
|
||||||
/> -->
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,18 +28,12 @@ export default defineComponent({
|
||||||
<li class="footer-list-item">
|
<li class="footer-list-item">
|
||||||
<p class="footer-list-content">
|
<p class="footer-list-content">
|
||||||
Contáctanos <br />
|
Contáctanos <br />
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
|
||||||
eiusmod tempor incididunt ut labore et dolore magna aliqua
|
|
||||||
<br /><br />
|
|
||||||
Horario: 9 a 14h. y de 15 a 18h.
|
Horario: 9 a 14h. y de 15 a 18h.
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="footer-list-item">
|
<li class="footer-list-item">
|
||||||
<p class="footer-list-content">
|
<p class="footer-list-content">
|
||||||
Lorem ipsum dolor sit amet,<br />
|
|
||||||
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
|
||||||
labore et dolore magna aliqua
|
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -43,7 +43,9 @@ export default defineComponent({
|
||||||
|
|
||||||
<nav-links />
|
<nav-links />
|
||||||
|
|
||||||
<user-area />
|
<q-no-ssr>
|
||||||
|
<user-area />
|
||||||
|
</q-no-ssr>
|
||||||
</div>
|
</div>
|
||||||
</q-header>
|
</q-header>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,27 +3,23 @@ import { defineComponent } from "vue";
|
||||||
|
|
||||||
import IconCart from "components/icons/IconCart.vue";
|
import IconCart from "components/icons/IconCart.vue";
|
||||||
import IconHamburger from "components/icons/IconHamburger.vue";
|
import IconHamburger from "components/icons/IconHamburger.vue";
|
||||||
/* import IconUser from "components/icons/IconUser.vue";
|
|
||||||
import DropdownGroup from "components/quasar-components/dropdown/DropdownGroup.vue";
|
import { useLocalStorage } from "src/hooks/useLocalStorage";
|
||||||
import DropdownItem from "components/quasar-components/dropdown/DropdownItem.vue"; */
|
|
||||||
import { storeToRefs } from "pinia";
|
|
||||||
import { useCartStore } from "src/stores/cart";
|
|
||||||
import { useMobileStore } from "stores/mobileNav";
|
import { useMobileStore } from "stores/mobileNav";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "user-area",
|
name: "user-area",
|
||||||
components: {
|
components: {
|
||||||
IconCart,
|
IconCart,
|
||||||
// IconUser,
|
|
||||||
// DropdownGroup,
|
|
||||||
// DropdownItem,
|
|
||||||
IconHamburger,
|
IconHamburger,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
|
const { getItem } = useLocalStorage();
|
||||||
|
|
||||||
const mobileStore = useMobileStore();
|
const mobileStore = useMobileStore();
|
||||||
const { handleOpenMobileNav } = mobileStore;
|
const { handleOpenMobileNav } = mobileStore;
|
||||||
const cartStore = useCartStore();
|
|
||||||
const { cartLength } = storeToRefs(cartStore);
|
const cartLength = getItem("cart").length;
|
||||||
|
|
||||||
return { handleOpenMobileNav, cartLength };
|
return { handleOpenMobileNav, cartLength };
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,6 +20,7 @@ export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const {
|
const {
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
setValues,
|
||||||
fields: { calendar, calendarAttrs, postalCode, postalCodeAttrs },
|
fields: { calendar, calendarAttrs, postalCode, postalCodeAttrs },
|
||||||
errors,
|
errors,
|
||||||
} = usePostalCalendar({ type: "home" });
|
} = usePostalCalendar({ type: "home" });
|
||||||
|
@ -43,6 +44,7 @@ export default defineComponent({
|
||||||
screenWidth,
|
screenWidth,
|
||||||
errors,
|
errors,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
setValues,
|
||||||
|
|
||||||
postalCode,
|
postalCode,
|
||||||
postalCodeAttrs,
|
postalCodeAttrs,
|
||||||
|
@ -86,15 +88,12 @@ export default defineComponent({
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="carousel-header-paragraph">
|
<p class="carousel-header-paragraph">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
|
||||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<form @submit="onSubmit" class="carousel-content-body">
|
<form @submit="onSubmit" class="carousel-content-body">
|
||||||
<div class="carousel-content-item">
|
<div class="carousel-content-item">
|
||||||
<!-- <Calendar /> -->
|
<Calendar :setValues="setValues">
|
||||||
<Calendar>
|
|
||||||
<q-input
|
<q-input
|
||||||
borderless
|
borderless
|
||||||
class="custom-date-input"
|
class="custom-date-input"
|
||||||
|
@ -109,7 +108,6 @@ export default defineComponent({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="carousel-content-item">
|
<div class="carousel-content-item">
|
||||||
<!-- <PostalCode /> -->
|
|
||||||
<PostalCode>
|
<PostalCode>
|
||||||
<q-input
|
<q-input
|
||||||
borderless
|
borderless
|
||||||
|
|
|
@ -26,8 +26,6 @@ export default defineComponent({
|
||||||
<LogoWhite />
|
<LogoWhite />
|
||||||
|
|
||||||
<p class="question-paragraph">
|
<p class="question-paragraph">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
|
||||||
eiusmod tempor incididunt ut labore et dolore...
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<StripeCheckout
|
||||||
|
ref="checkoutRef"
|
||||||
|
mode="payment"
|
||||||
|
:pk="pK"
|
||||||
|
:line-items="cartItems"
|
||||||
|
:success-url="successURL"
|
||||||
|
:cancel-url="cancelURL"
|
||||||
|
@loading="(v) => (loading = v)"
|
||||||
|
style="display: none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { defineComponent, ref, toRefs } from "vue";
|
||||||
|
|
||||||
|
import { useCartStore } from "src/stores/cart";
|
||||||
|
import { onUpdated } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "StripeCheckoutComponent",
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
submitLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
onSubmit: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
cartItems: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup({ submitLoading, cartItems }) {
|
||||||
|
const cartStore = useCartStore();
|
||||||
|
const { checkoutRef } = storeToRefs(cartStore);
|
||||||
|
|
||||||
|
const loading = toRefs(submitLoading);
|
||||||
|
const pK = ref(
|
||||||
|
"pk_test_51OZaJdIK1lTlG93d2y0B81n4XrjvjQwqfIUZ7ggb9wEBa1e4h34GlYFYPwjtGl3OUT7DJZlVNX9EMXaCdOBkIC3T007mLnfvCu"
|
||||||
|
);
|
||||||
|
|
||||||
|
onUpdated(() => {
|
||||||
|
console.log(checkoutRef.value);
|
||||||
|
console.log(cartItems);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
pK,
|
||||||
|
loading,
|
||||||
|
checkoutRef,
|
||||||
|
successURL: ref("/checkout/success"),
|
||||||
|
cancelURL: ref("/checkout/cancel"),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss"></style>
|
|
@ -46,22 +46,35 @@ export default defineComponent({
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: Number,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup({ price, discount }) {
|
setup({ price, discount }) {
|
||||||
const isLoaded = ref(false);
|
const isLoaded = ref(false);
|
||||||
|
const isError = ref(false);
|
||||||
const percent = +discount / 100;
|
const percent = +discount / 100;
|
||||||
//const priceWithoutLetter = ~~price.replaceAll("€", "");
|
//const priceWithoutLetter = ~~price?.replaceAll("€", "");
|
||||||
const priceWithoutLetter = price;
|
const priceWithoutLetter = price;
|
||||||
const finalValue = ~~(priceWithoutLetter - priceWithoutLetter * percent);
|
const finalValue = ~~(priceWithoutLetter - priceWithoutLetter * percent);
|
||||||
|
console.log(price);
|
||||||
|
|
||||||
const onLoad = () => {
|
const onLoad = () => {
|
||||||
isLoaded.value = true;
|
isLoaded.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { onLoad, isLoaded, finalValue, priceWithoutLetter };
|
const onError = () => {
|
||||||
|
isError.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onLoad,
|
||||||
|
onError,
|
||||||
|
isLoaded,
|
||||||
|
isError,
|
||||||
|
finalValue,
|
||||||
|
priceWithoutLetter,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -82,10 +95,11 @@ export default defineComponent({
|
||||||
<img
|
<img
|
||||||
class="card-img"
|
class="card-img"
|
||||||
:class="[imgClass]"
|
:class="[imgClass]"
|
||||||
:src="imgSrc ? imgSrc.replace('https://drive.google.com/file/d/', 'https://drive.google.com/thumbnail?id=').replace('/view?usp=drive_link', '') : '../../assets/empty-img.jpg'"
|
:src="imgSrc && !isError ? imgSrc : '../../assets/empty-img.jpg'"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
:key="imgSrc"
|
:key="imgSrc"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
|
@error="onError"
|
||||||
/>
|
/>
|
||||||
<q-skeleton
|
<q-skeleton
|
||||||
v-if="!isLoaded"
|
v-if="!isLoaded"
|
||||||
|
@ -105,7 +119,7 @@ export default defineComponent({
|
||||||
|
|
||||||
<div class="card-values">
|
<div class="card-values">
|
||||||
<p class="price" v-if="finalValue">{{ finalValue }}€</p>
|
<p class="price" v-if="finalValue">{{ finalValue }}€</p>
|
||||||
<p class="price offer tachado" v-if="price !== finalValue">
|
<p class="price offer tachado" v-if="+price !== finalValue">
|
||||||
{{ price }}€
|
{{ price }}€
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,6 +30,7 @@ export default defineComponent({
|
||||||
setup({ modalItem, typeModal }) {
|
setup({ modalItem, typeModal }) {
|
||||||
const {
|
const {
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
setValues,
|
||||||
fields: {
|
fields: {
|
||||||
calendar,
|
calendar,
|
||||||
calendarAttrs,
|
calendarAttrs,
|
||||||
|
@ -61,6 +62,7 @@ export default defineComponent({
|
||||||
onSubmit,
|
onSubmit,
|
||||||
modalStore,
|
modalStore,
|
||||||
modalTextContent,
|
modalTextContent,
|
||||||
|
setValues,
|
||||||
|
|
||||||
postalCode,
|
postalCode,
|
||||||
postalCodeAttrs,
|
postalCodeAttrs,
|
||||||
|
@ -121,7 +123,7 @@ export default defineComponent({
|
||||||
v-if="modalItem === 'isOpenAvailability'"
|
v-if="modalItem === 'isOpenAvailability'"
|
||||||
class="modal-body-availability"
|
class="modal-body-availability"
|
||||||
>
|
>
|
||||||
<Calendar>
|
<Calendar :setValues="setValues">
|
||||||
<q-input
|
<q-input
|
||||||
borderless
|
borderless
|
||||||
class="custom-date-input"
|
class="custom-date-input"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const getDate = new Date();
|
||||||
|
export const currentDay = getDate.getDate().toString().padStart(2, "0");
|
||||||
|
export const currentMonth = (getDate.getMonth() + 1)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0");
|
||||||
|
export const currentYear = getDate.getFullYear();
|
||||||
|
export const fullCurrentDate = `${currentYear}/${currentMonth}/${currentDay}`;
|
|
@ -0,0 +1,7 @@
|
||||||
|
export function invertDate(date = "") {
|
||||||
|
const regex = /-|\//g;
|
||||||
|
const [day = "", month = " ", year = ""] = date.split(regex);
|
||||||
|
const invertedDate = `${year}-${month}-${day}`;
|
||||||
|
|
||||||
|
return invertedDate;
|
||||||
|
}
|
|
@ -26,10 +26,20 @@ export function quasarNotify({ message = "", type, timeout = 1000 }) {
|
||||||
icon: "report_problem",
|
icon: "report_problem",
|
||||||
timeout,
|
timeout,
|
||||||
}),
|
}),
|
||||||
|
info: () => Notify.create({
|
||||||
|
message,
|
||||||
|
color: "info",
|
||||||
|
position: "top",
|
||||||
|
icon: "info",
|
||||||
|
timeout,
|
||||||
|
}),
|
||||||
|
default: () => {
|
||||||
|
console.error(`Type is invalid! TYPE: ${type}`)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
return obj[type]() || console.error(`Type is invalid! TYPE: ${type}`);
|
return obj[type]() || obj['default']();
|
||||||
}
|
}
|
||||||
console.error("Type is required, success, warning or erro");
|
console.error("Type is required, success, warning or erro");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
import { toTypedSchema } from "@vee-validate/zod";
|
||||||
|
import { useForm } from "vee-validate";
|
||||||
|
import { computed, reactive, ref } from "vue";
|
||||||
|
|
||||||
|
import { useFormStore } from "src/stores/forms";
|
||||||
|
import { checkoutSchema } from "src/utils/zod/schemas";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { useLocalStorage } from "./useLocalStorage";
|
||||||
|
|
||||||
|
export function useCheckoutForm() {
|
||||||
|
const { getItem } = useLocalStorage();
|
||||||
|
const { push } = useRouter()
|
||||||
|
|
||||||
|
const formStore = useFormStore();
|
||||||
|
const { handleCheckoutData } = formStore;
|
||||||
|
const { meta, errors, handleSubmit, defineField, resetForm } = useForm({
|
||||||
|
validationSchema: toTypedSchema(checkoutSchema),
|
||||||
|
initialValues: {
|
||||||
|
paymentMethod: "stripe",
|
||||||
|
terms: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
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");
|
||||||
|
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");
|
||||||
|
const [senderNotes, senderNotesAttrs] = defineField("senderNotes");
|
||||||
|
const [paymentMethod, paymentMethodAttrs] = defineField("paymentMethod");
|
||||||
|
const [terms, termsAttrs] = defineField("terms");
|
||||||
|
|
||||||
|
const provinceOptions = ref([
|
||||||
|
{ code: "01", name: "Araba/Álava" },
|
||||||
|
{ code: "02", name: "Albacete" },
|
||||||
|
{ code: "03", name: "Alicante/Alacant" },
|
||||||
|
{ code: "04", name: "Almería" },
|
||||||
|
{ code: "05", name: "Ávila" },
|
||||||
|
{ code: "06", name: "Badajoz" },
|
||||||
|
{ code: "07", name: "Balears, Illes" },
|
||||||
|
{ code: "08", name: "Barcelona" },
|
||||||
|
{ code: "09", name: "Burgos" },
|
||||||
|
{ code: "10", name: "Cáceres" },
|
||||||
|
{ code: "11", name: "Cádiz" },
|
||||||
|
{ code: "12", name: "Castellón/Castelló" },
|
||||||
|
{ code: "13", name: "Ciudad Real" },
|
||||||
|
{ code: "14", name: "Córdoba" },
|
||||||
|
{ code: "15", name: "Coruña, A" },
|
||||||
|
{ code: "16", name: "Cuenca" },
|
||||||
|
{ code: "17", name: "Girona" },
|
||||||
|
{ code: "18", name: "Granada" },
|
||||||
|
{ code: "19", name: "Guadalajara" },
|
||||||
|
{ code: "20", name: "Gipuzkoa" },
|
||||||
|
{ code: "21", name: "Huelva" },
|
||||||
|
{ code: "22", name: "Huesca" },
|
||||||
|
{ code: "23", name: "Jaén" },
|
||||||
|
{ code: "24", name: "León" },
|
||||||
|
{ code: "25", name: "Lleida" },
|
||||||
|
{ code: "26", name: "Rioja, La" },
|
||||||
|
{ code: "27", name: "Lugo" },
|
||||||
|
{ code: "28", name: "Madrid" },
|
||||||
|
{ code: "29", name: "Málaga" },
|
||||||
|
{ code: "30", name: "Murcia" },
|
||||||
|
{ code: "31", name: "Navarra" },
|
||||||
|
{ code: "32", name: "Ourense" },
|
||||||
|
{ code: "33", name: "Asturias" },
|
||||||
|
{ code: "34", name: "Palencia" },
|
||||||
|
{ code: "35", name: "Palmas, Las" },
|
||||||
|
{ code: "36", name: "Pontevedra" },
|
||||||
|
{ code: "37", name: "Salamanca" },
|
||||||
|
{ code: "38", name: "Santa Cruz de Tenerife" },
|
||||||
|
{ code: "39", name: "Cantabria" },
|
||||||
|
{ code: "40", name: "Segovia" },
|
||||||
|
{ code: "41", name: "Sevilla" },
|
||||||
|
{ code: "42", name: "Soria" },
|
||||||
|
{ code: "43", name: "Tarragona" },
|
||||||
|
{ code: "44", name: "Teruel" },
|
||||||
|
{ code: "45", name: "Toledo" },
|
||||||
|
{ code: "46", name: "Valencia/València" },
|
||||||
|
{ code: "47", name: "Valladolid" },
|
||||||
|
{ code: "48", name: "Bizkaia" },
|
||||||
|
{ code: "49", name: "Zamora" },
|
||||||
|
{ code: "50", name: "Zaragoza" },
|
||||||
|
{ code: "51", name: "Ceuta" },
|
||||||
|
{ code: "52", name: "Melilla" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
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 handleClickStep = (value) => {
|
||||||
|
stepActive["data"] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkoutBlock = ref(true);
|
||||||
|
const onSubmit = handleSubmit((values) => {
|
||||||
|
handleCheckoutData(values);
|
||||||
|
stepList.data[2].active = true;
|
||||||
|
checkoutBlock.value = false;
|
||||||
|
resetForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
const cart = getItem("cart");
|
||||||
|
const totalPrice = ref(0)
|
||||||
|
totalPrice.value = cart?.reduce((acc, { price }) => {
|
||||||
|
if (price) {
|
||||||
|
const priceWithoutLetter = price?.replace("€", "");
|
||||||
|
return +priceWithoutLetter + acc;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleClickStep,
|
||||||
|
provinceOptions,
|
||||||
|
stepsFormated,
|
||||||
|
stepList,
|
||||||
|
checkoutBlock,
|
||||||
|
cart,
|
||||||
|
totalPrice,
|
||||||
|
formState: {
|
||||||
|
meta,
|
||||||
|
errors,
|
||||||
|
onSubmit,
|
||||||
|
submitLoading: ref(false),
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { LocalStorage } from "quasar";
|
||||||
|
|
||||||
|
export function useLocalStorage() {
|
||||||
|
const addItem = (key, value) => {
|
||||||
|
LocalStorage.set(`@${key}`, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItem = (key) => {
|
||||||
|
const data = JSON.parse(LocalStorage.getItem(`@${key}`));
|
||||||
|
|
||||||
|
|
||||||
|
return (data || []);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeItem = (key) => {
|
||||||
|
LocalStorage.remove(`@${key}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
addItem,
|
||||||
|
getItem,
|
||||||
|
removeItem,
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import { toTypedSchema } from "@vee-validate/zod";
|
import { toTypedSchema } from "@vee-validate/zod";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useForm } from "vee-validate";
|
import { useForm } from "vee-validate";
|
||||||
import { watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
import { apiBack } from "src/boot/axios";
|
import { invertDate } from "src/functions/invertDate";
|
||||||
import { quasarNotify } from "src/functions/quasarNotify";
|
import { quasarNotify } from "src/functions/quasarNotify";
|
||||||
import { useCartStore } from "src/stores/cart";
|
import { useCartStore } from "src/stores/cart";
|
||||||
import { useFormStore } from "src/stores/forms";
|
import { useFormStore } from "src/stores/forms";
|
||||||
|
@ -13,38 +13,50 @@ import { useRangePriceStore } from "src/stores/rangePrice";
|
||||||
import { availabilitySchema } from "src/utils/zod/schemas";
|
import { availabilitySchema } from "src/utils/zod/schemas";
|
||||||
import { rangePriceSchema } from "src/utils/zod/schemas/rangePriceSchema";
|
import { rangePriceSchema } from "src/utils/zod/schemas/rangePriceSchema";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook for managing the postal and calendar functionality.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for the hook.
|
||||||
|
* @param {string} options.modalItem - The modal item isOpenAvailability || isOpenFilters.
|
||||||
|
* @param {string} options.type - The type of the hook. home || product || availability || filter
|
||||||
|
* @returns {Object} - The hook functions and data.
|
||||||
|
*/
|
||||||
export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
||||||
const { push } = useRouter();
|
const route = useRoute();
|
||||||
|
const { push } = useRouter()
|
||||||
|
|
||||||
const rangePriceStore = useRangePriceStore();
|
const rangePriceStore = useRangePriceStore();
|
||||||
const { rangeValue } = storeToRefs(rangePriceStore);
|
const { rangeValue } = storeToRefs(rangePriceStore);
|
||||||
|
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
const { isOpenAvailability } = storeToRefs(modalStore);
|
const { openModal } = modalStore
|
||||||
|
|
||||||
const formStore = useFormStore();
|
const formStore = useFormStore();
|
||||||
const { availability, sortProductFilters } = storeToRefs(formStore);
|
const { sortProductFilters } = storeToRefs(formStore);
|
||||||
|
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const { addToCart, getProducts } = cartStore;
|
const { addToCart, getProducts } = cartStore;
|
||||||
const { currentProduct, products } = storeToRefs(cartStore);
|
const { products, homeSection } = storeToRefs(cartStore);
|
||||||
|
|
||||||
const min = 0;
|
const min = 0;
|
||||||
const max = 200;
|
const max = 200;
|
||||||
|
const category = ref(route.path.split("/")[2])
|
||||||
|
|
||||||
const { handleSubmit, handleReset, defineField, errors } = useForm({
|
const { handleSubmit, handleReset, defineField, errors, setValues } = useForm(
|
||||||
validationSchema: toTypedSchema(
|
{
|
||||||
type !== "filter" ? availabilitySchema : rangePriceSchema
|
validationSchema: toTypedSchema(
|
||||||
),
|
type !== "filter" ? availabilitySchema : rangePriceSchema
|
||||||
initialValues: {
|
),
|
||||||
range: {
|
initialValues: {
|
||||||
min,
|
range: {
|
||||||
max,
|
min,
|
||||||
|
max,
|
||||||
|
},
|
||||||
|
postalCode: "",
|
||||||
|
date: "",
|
||||||
},
|
},
|
||||||
postalCode: "",
|
}
|
||||||
date: "",
|
);
|
||||||
},
|
|
||||||
});
|
|
||||||
const [calendar, calendarAttrs] = defineField("date");
|
const [calendar, calendarAttrs] = defineField("date");
|
||||||
const [postalCode, postalCodeAttrs] = defineField("postalCode");
|
const [postalCode, postalCodeAttrs] = defineField("postalCode");
|
||||||
const [priceRange, priceRangeAttrs] = defineField("range");
|
const [priceRange, priceRangeAttrs] = defineField("range");
|
||||||
|
@ -56,6 +68,8 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
||||||
quasarNotify({ message: newErrors.postalCode, type: "erro" }),
|
quasarNotify({ message: newErrors.postalCode, type: "erro" }),
|
||||||
date: () => quasarNotify({ message: newErrors.date, type: "erro" }),
|
date: () => quasarNotify({ message: newErrors.date, type: "erro" }),
|
||||||
range: () => quasarNotify({ message: newErrors.range, type: "erro" }),
|
range: () => quasarNotify({ message: newErrors.range, type: "erro" }),
|
||||||
|
dedication: () =>
|
||||||
|
quasarNotify({ message: newErrors.dedication, type: "erro" }),
|
||||||
};
|
};
|
||||||
const keys = Object.keys(newErrors);
|
const keys = Object.keys(newErrors);
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
|
@ -63,47 +77,73 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[() => route.path, () => sortProductFilters.value],
|
||||||
|
([newPath]) => {
|
||||||
|
const categoryPath = newPath.split("/")[2];
|
||||||
|
category.value = categoryPath;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const onSubmit = handleSubmit((values) => {
|
const onSubmit = handleSubmit((values) => {
|
||||||
const postalAndDateParams = {
|
const postalAndDateParams = {
|
||||||
postalCode: values.postalCode,
|
postalCode: values.postalCode,
|
||||||
dateExpired: values.date,
|
dateExpired: invertDate(values.date),
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryObj = {
|
||||||
|
plantas: "Floranet Plantas",
|
||||||
|
ramos: "Floranet Ramos",
|
||||||
};
|
};
|
||||||
|
|
||||||
const objVal = {
|
const objVal = {
|
||||||
home: async () => {
|
home: async () => {
|
||||||
console.log(type);
|
console.log(type);
|
||||||
|
|
||||||
getProducts(
|
await getProducts(
|
||||||
{
|
{
|
||||||
itens: 30,
|
|
||||||
category: 1,
|
|
||||||
postalCode: values.postalCode,
|
postalCode: values.postalCode,
|
||||||
dateExpired: values.date,
|
dateExpired: invertDate(values.date),
|
||||||
},
|
},
|
||||||
() => push("/categoria/ramos")
|
() => homeSection.value.scrollIntoView()
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
product: async () => {
|
product: async () => {
|
||||||
console.log(type);
|
console.log(type);
|
||||||
|
|
||||||
try {
|
await getProducts(postalAndDateParams);
|
||||||
const {
|
|
||||||
data: { data },
|
const hasProduct = products.value.data.some((item) => {
|
||||||
} = await apiBack.get(`products/slug/${currentProduct.value.id}`, {
|
const date = new Date(item.dateExpired);
|
||||||
params: postalAndDateParams,
|
const day = date.getDate();
|
||||||
});
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||||
} catch (error) {
|
const year = date.getFullYear();
|
||||||
console.error(error);
|
const dateExpired = `${day}/${month}/${year}`;
|
||||||
push("/");
|
|
||||||
|
const id = +route.path.split('/')[2];
|
||||||
|
|
||||||
|
return item.postalCode === values.postalCode && item.id === id && values.date <= dateExpired
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasProduct) {
|
||||||
|
push('/categoria/ramos')
|
||||||
|
quasarNotify({ message: 'Seleccione una nueva fecha y un nuevo código postal.', type: 'warning' })
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
openModal({ modal: 'availability' })
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addToCart(products.value.current, dedication)
|
||||||
},
|
},
|
||||||
availability: async () => {
|
availability: async () => {
|
||||||
console.log(type);
|
console.log(type);
|
||||||
|
|
||||||
getProducts({
|
await getProducts({
|
||||||
itens: 20,
|
|
||||||
postalCode: values.postalCode,
|
postalCode: values.postalCode,
|
||||||
dateExpired: values.date,
|
dateExpired: invertDate(values.date),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
filter: async () => {
|
filter: async () => {
|
||||||
|
@ -112,26 +152,21 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
||||||
rangeValue.value.max = values.range.max;
|
rangeValue.value.max = values.range.max;
|
||||||
rangeValue.value.min = values.range.min;
|
rangeValue.value.min = values.range.min;
|
||||||
|
|
||||||
const category = sortProductFilters.value.category;
|
|
||||||
const categoryObj = {
|
|
||||||
plantas: 1,
|
|
||||||
ramos: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
itens: 20,
|
type: categoryObj[category.value],
|
||||||
category: categoryObj[category],
|
|
||||||
minPrice: values.range.min,
|
minPrice: values.range.min,
|
||||||
maxPrice: values.range.max,
|
maxPrice: values.range.max,
|
||||||
};
|
};
|
||||||
|
|
||||||
getProducts(params);
|
await getProducts(params);
|
||||||
|
},
|
||||||
|
default: () => {
|
||||||
|
console.error(
|
||||||
|
`INVALID TYPE! TYPE: ${type}, ONLY HOME, PRODUCT AND FILTER ARE VALID!`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
objVal[type]() ||
|
objVal[type]() || objVal["default"]();
|
||||||
console.error(
|
|
||||||
`INVALID TYPE! TYPE: ${type}, ONLY HOME, PRODUCT AND FILTER ARE VALID!`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (modalItem) {
|
if (modalItem) {
|
||||||
modalStore[modalItem] = false;
|
modalStore[modalItem] = false;
|
||||||
|
@ -141,6 +176,7 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
setValues,
|
||||||
modalStore,
|
modalStore,
|
||||||
fields: {
|
fields: {
|
||||||
calendar,
|
calendar,
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { useCartStore } from "src/stores/cart";
|
||||||
|
import { useModalStore } from "src/stores/modalStore";
|
||||||
|
import { watch } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import { usePostalCalendar } from "./usePostalCalendar";
|
||||||
|
|
||||||
|
export function useProductPage() {
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const {
|
||||||
|
fields: { dedication, dedicationAttrs },
|
||||||
|
} = usePostalCalendar({ modalItem: "isOpenAvailability" });
|
||||||
|
|
||||||
|
const modalStore = useModalStore();
|
||||||
|
const { openModal } = modalStore;
|
||||||
|
|
||||||
|
const cartStore = useCartStore();
|
||||||
|
const { getProduct, getProducts } = cartStore;
|
||||||
|
const { products, featuredProducts, addCartLoadingBtn } =
|
||||||
|
storeToRefs(cartStore);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => products.value.current?.type,
|
||||||
|
(newCategory) => {
|
||||||
|
getProducts({
|
||||||
|
// type: newCategory,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.id,
|
||||||
|
(newId) => {
|
||||||
|
getProduct(newId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkImageValidity = (imageLink) => {
|
||||||
|
const validExtensions = [".jpg", ".jpeg", ".png"];
|
||||||
|
|
||||||
|
if (imageLink) {
|
||||||
|
const extension = imageLink.substring(imageLink.lastIndexOf("."));
|
||||||
|
return validExtensions.includes(extension.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { checkImageValidity, openModal }
|
||||||
|
}
|
|
@ -27,9 +27,11 @@ export default defineComponent({
|
||||||
</q-no-ssr>
|
</q-no-ssr>
|
||||||
<mobile-nav />
|
<mobile-nav />
|
||||||
|
|
||||||
<q-page-container class="no-padding more product-layout">
|
<q-no-ssr>
|
||||||
<router-view />
|
<q-page-container class="no-padding more product-layout">
|
||||||
</q-page-container>
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
</q-no-ssr>
|
||||||
|
|
||||||
<reasons-section />
|
<reasons-section />
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ export function mockGenerator({ length }) {
|
||||||
slug: fakerES.commerce.isbn({ separator: "-", variant: 13 }),
|
slug: fakerES.commerce.isbn({ separator: "-", variant: 13 }),
|
||||||
category: fakerES.number.int({ min: 1, max: 2 }),
|
category: fakerES.number.int({ min: 1, max: 2 }),
|
||||||
postalCode: "12345",
|
postalCode: "12345",
|
||||||
dateExpired: "30/01/2024",
|
dateExpired: "2024-01-30",
|
||||||
images: Array.from(
|
images: Array.from(
|
||||||
{ length: fakerES.number.int({ min: 2, max: 6 }) },
|
{ length: fakerES.number.int({ min: 2, max: 6 }) },
|
||||||
() => fakerES.image.urlPicsumPhotos()
|
() => fakerES.image.urlPicsumPhotos()
|
||||||
|
|
|
@ -12,8 +12,10 @@ 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 { useCartStore } from "src/stores/cart";
|
import { useCartStore } from "src/stores/cart";
|
||||||
import { useFormStore } from "src/stores/forms";
|
import { useFormStore } from "src/stores/forms";
|
||||||
|
import { useMobileStore } from "src/stores/mobileNav";
|
||||||
import { useModalStore } from "src/stores/modalStore";
|
import { useModalStore } from "src/stores/modalStore";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -32,6 +34,9 @@ export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const mobileStore = useMobileStore();
|
||||||
|
const { screenWidth } = storeToRefs(mobileStore);
|
||||||
|
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
const { openModal } = modalStore;
|
const { openModal } = modalStore;
|
||||||
|
|
||||||
|
@ -66,8 +71,8 @@ export default defineComponent({
|
||||||
latest: "más recientes",
|
latest: "más recientes",
|
||||||
};
|
};
|
||||||
const categoryObj = {
|
const categoryObj = {
|
||||||
plantas: 1,
|
plantas: "Floranet Plantas",
|
||||||
ramos: 2,
|
ramos: "Floranet Ramos",
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(availability, (newDate) => {
|
watch(availability, (newDate) => {
|
||||||
|
@ -83,14 +88,13 @@ export default defineComponent({
|
||||||
sortProductFilters.value.category = categoryPath;
|
sortProductFilters.value.category = categoryPath;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
category: categoryObj[categoryPath],
|
type: categoryObj[categoryPath],
|
||||||
itens: window.screen.width <= 445 ? 16 : 20,
|
|
||||||
};
|
};
|
||||||
const paramsObj = {
|
const paramsObj = {
|
||||||
"lowest-price": () => (params.lowPrice = 1),
|
"lowest-price": () => (params.lowPrice = 1),
|
||||||
"highest-price": () => (params.bigPrice = 1),
|
"highest-price": () => (params.bigPrice = 1),
|
||||||
latest: () => (params.isNew = 1),
|
latest: () => (params.isNew = 1),
|
||||||
// recommended: () => params.featured = 1,
|
recommended: () => (params.recommend = 1),
|
||||||
};
|
};
|
||||||
if (newOrder) {
|
if (newOrder) {
|
||||||
paramsObj[newOrder]();
|
paramsObj[newOrder]();
|
||||||
|
@ -104,8 +108,7 @@ export default defineComponent({
|
||||||
const categoryPath = route.path.split("/")[2];
|
const categoryPath = route.path.split("/")[2];
|
||||||
|
|
||||||
await getProducts({
|
await getProducts({
|
||||||
category: categoryObj[categoryPath],
|
type: categoryObj[categoryPath],
|
||||||
itens: window.screen.width <= 445 ? 16 : 20,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,11 +125,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sortProductFilters,
|
|
||||||
openOrderFilter,
|
openOrderFilter,
|
||||||
openModal,
|
openModal,
|
||||||
|
sortProductFilters,
|
||||||
availability,
|
availability,
|
||||||
isOpenOrder,
|
isOpenOrder,
|
||||||
|
screenWidth,
|
||||||
modalStore,
|
modalStore,
|
||||||
orderText,
|
orderText,
|
||||||
products,
|
products,
|
||||||
|
@ -145,9 +149,6 @@ export default defineComponent({
|
||||||
{{ sortProductFilters.category }} para obsequiar
|
{{ sortProductFilters.category }} para obsequiar
|
||||||
</h3>
|
</h3>
|
||||||
<p class="product-header-paragraph">
|
<p class="product-header-paragraph">
|
||||||
Descripción SEO: Lorem ipsum dolor sit amet, consectetur
|
|
||||||
adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
|
||||||
dolore magna aliqua.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -218,25 +219,14 @@ export default defineComponent({
|
||||||
|
|
||||||
<div class="products-section-body">
|
<div class="products-section-body">
|
||||||
<Container cardContainer class="category-container">
|
<Container cardContainer class="category-container">
|
||||||
<template
|
<template v-for="item in products.data" :key="item?.id">
|
||||||
v-for="{
|
|
||||||
images,
|
|
||||||
discount,
|
|
||||||
isNew,
|
|
||||||
name,
|
|
||||||
price,
|
|
||||||
slug,
|
|
||||||
id,
|
|
||||||
} in products.data.products"
|
|
||||||
:key="id"
|
|
||||||
>
|
|
||||||
<Card
|
<Card
|
||||||
:price="price"
|
v-if="item"
|
||||||
:title="name"
|
:price="item.price"
|
||||||
:discount="discount"
|
:title="item.name"
|
||||||
:imgSrc="images[0]"
|
:imgSrc="item.image"
|
||||||
:isNew="isNew"
|
:isNew="item.isNew"
|
||||||
:id="slug"
|
:id="item.id"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import { toTypedSchema } from "@vee-validate/zod";
|
import { defineComponent, onBeforeMount, ref } from "vue";
|
||||||
import { storeToRefs } from "pinia";
|
|
||||||
import { useForm } from "vee-validate";
|
|
||||||
import { computed, defineComponent, reactive, ref } from "vue";
|
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
import Container from "src/components/ui/Container.vue";
|
import Container from "src/components/ui/Container.vue";
|
||||||
import { useCartStore } from "src/stores/cart";
|
import { useCheckoutForm } from "src/hooks/useCheckoutForm";
|
||||||
import { useFormStore } from "src/stores/forms";
|
|
||||||
import { checkoutSchema } from "src/utils/zod/schemas/checkoutSchema";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "CheckoutPage",
|
name: "CheckoutPage",
|
||||||
|
@ -17,150 +12,75 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const {
|
||||||
const cartStore = useCartStore();
|
provinceOptions,
|
||||||
const { cart, cartList, totalPrice, cartLength } = storeToRefs(cartStore);
|
handleClickStep,
|
||||||
|
stepsFormated,
|
||||||
if (cartLength.value === 0) return push("/");
|
stepList,
|
||||||
|
checkoutBlock,
|
||||||
const formStore = useFormStore();
|
cart,
|
||||||
const { handleCheckoutData } = formStore;
|
totalPrice,
|
||||||
const { meta, errors, handleSubmit, defineField, resetForm } = useForm({
|
formState: { errors, meta, onSubmit, submitLoading },
|
||||||
validationSchema: toTypedSchema(checkoutSchema),
|
fields: {
|
||||||
initialValues: {
|
name,
|
||||||
paymentMethod: "stripe",
|
nameAttrs,
|
||||||
terms: false,
|
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,
|
||||||
},
|
},
|
||||||
});
|
} = useCheckoutForm();
|
||||||
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");
|
|
||||||
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");
|
|
||||||
const [senderNotes, senderNotesAttrs] = defineField("senderNotes");
|
|
||||||
const [paymentMethod, paymentMethodAttrs] = defineField("paymentMethod");
|
|
||||||
const [terms, termsAttrs] = defineField("terms");
|
|
||||||
|
|
||||||
const stepActive = reactive({ data: 1 });
|
onBeforeMount(() => {
|
||||||
const stepList = reactive({
|
if (cart.length === 0) return push("/");
|
||||||
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 checkoutBlock = ref(true);
|
|
||||||
|
|
||||||
const onSubmit = handleSubmit((values) => {
|
|
||||||
handleCheckoutData(values);
|
|
||||||
stepList.data[2].active = true;
|
|
||||||
checkoutBlock.value = false;
|
|
||||||
resetForm();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClickStep = (value) => {
|
const isError = ref(false);
|
||||||
stepActive["data"] = value;
|
const onError = () => {
|
||||||
|
isError.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const stepsFormated = computed(() => {
|
|
||||||
return stepList["data"].map((step) => {
|
|
||||||
if (step.value === stepActive["data"]) {
|
|
||||||
return { ...step, active: true };
|
|
||||||
}
|
|
||||||
return step;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const provinceOptions = ref([
|
|
||||||
{ code: "01", name: "Araba/Álava" },
|
|
||||||
{ code: "02", name: "Albacete" },
|
|
||||||
{ code: "03", name: "Alicante/Alacant" },
|
|
||||||
{ code: "04", name: "Almería" },
|
|
||||||
{ code: "05", name: "Ávila" },
|
|
||||||
{ code: "06", name: "Badajoz" },
|
|
||||||
{ code: "07", name: "Balears, Illes" },
|
|
||||||
{ code: "08", name: "Barcelona" },
|
|
||||||
{ code: "09", name: "Burgos" },
|
|
||||||
{ code: "10", name: "Cáceres" },
|
|
||||||
{ code: "11", name: "Cádiz" },
|
|
||||||
{ code: "12", name: "Castellón/Castelló" },
|
|
||||||
{ code: "13", name: "Ciudad Real" },
|
|
||||||
{ code: "14", name: "Córdoba" },
|
|
||||||
{ code: "15", name: "Coruña, A" },
|
|
||||||
{ code: "16", name: "Cuenca" },
|
|
||||||
{ code: "17", name: "Girona" },
|
|
||||||
{ code: "18", name: "Granada" },
|
|
||||||
{ code: "19", name: "Guadalajara" },
|
|
||||||
{ code: "20", name: "Gipuzkoa" },
|
|
||||||
{ code: "21", name: "Huelva" },
|
|
||||||
{ code: "22", name: "Huesca" },
|
|
||||||
{ code: "23", name: "Jaén" },
|
|
||||||
{ code: "24", name: "León" },
|
|
||||||
{ code: "25", name: "Lleida" },
|
|
||||||
{ code: "26", name: "Rioja, La" },
|
|
||||||
{ code: "27", name: "Lugo" },
|
|
||||||
{ code: "28", name: "Madrid" },
|
|
||||||
{ code: "29", name: "Málaga" },
|
|
||||||
{ code: "30", name: "Murcia" },
|
|
||||||
{ code: "31", name: "Navarra" },
|
|
||||||
{ code: "32", name: "Ourense" },
|
|
||||||
{ code: "33", name: "Asturias" },
|
|
||||||
{ code: "34", name: "Palencia" },
|
|
||||||
{ code: "35", name: "Palmas, Las" },
|
|
||||||
{ code: "36", name: "Pontevedra" },
|
|
||||||
{ code: "37", name: "Salamanca" },
|
|
||||||
{ code: "38", name: "Santa Cruz de Tenerife" },
|
|
||||||
{ code: "39", name: "Cantabria" },
|
|
||||||
{ code: "40", name: "Segovia" },
|
|
||||||
{ code: "41", name: "Sevilla" },
|
|
||||||
{ code: "42", name: "Soria" },
|
|
||||||
{ code: "43", name: "Tarragona" },
|
|
||||||
{ code: "44", name: "Teruel" },
|
|
||||||
{ code: "45", name: "Toledo" },
|
|
||||||
{ code: "46", name: "Valencia/València" },
|
|
||||||
{ code: "47", name: "Valladolid" },
|
|
||||||
{ code: "48", name: "Bizkaia" },
|
|
||||||
{ code: "49", name: "Zamora" },
|
|
||||||
{ code: "50", name: "Zaragoza" },
|
|
||||||
{ code: "51", name: "Ceuta" },
|
|
||||||
{ code: "52", name: "Melilla" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleClickStep,
|
handleClickStep,
|
||||||
stepsFormated,
|
|
||||||
onSubmit,
|
onSubmit,
|
||||||
stepList,
|
onError,
|
||||||
|
|
||||||
|
checkoutBlock,
|
||||||
|
stepsFormated,
|
||||||
provinceOptions,
|
provinceOptions,
|
||||||
totalPrice,
|
totalPrice,
|
||||||
cartList,
|
stepList,
|
||||||
|
|
||||||
step: ref(1),
|
|
||||||
cart,
|
cart,
|
||||||
checkoutBlock,
|
step: ref(1),
|
||||||
|
submitLoading,
|
||||||
|
successURL: ref(""),
|
||||||
|
cancelURL: ref(""),
|
||||||
meta,
|
meta,
|
||||||
errors,
|
errors,
|
||||||
|
isError,
|
||||||
|
|
||||||
name,
|
name,
|
||||||
nameAttrs,
|
nameAttrs,
|
||||||
surname,
|
surname,
|
||||||
|
@ -199,19 +119,18 @@ export default defineComponent({
|
||||||
<Container tag="section">
|
<Container tag="section">
|
||||||
<header class="header-title" :class="!checkoutBlock && 'success'">
|
<header class="header-title" :class="!checkoutBlock && 'success'">
|
||||||
<h1 class="pege-title" v-if="checkoutBlock">
|
<h1 class="pege-title" v-if="checkoutBlock">
|
||||||
{{ checkoutBlock && "¿A quién y dónde lo entregamos?" }}
|
{{
|
||||||
{{ !checkoutBlock && "¡Muchas gracias Jerom!" }}
|
checkoutBlock
|
||||||
|
? "¿A quién y dónde lo entregamos?"
|
||||||
|
: '"¡Muchas gracias Jerom!"'
|
||||||
|
}}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="page-subtitle checkout" v-if="checkoutBlock">
|
<p class="page-subtitle checkout" v-if="checkoutBlock">
|
||||||
{{
|
{{
|
||||||
checkoutBlock &&
|
checkoutBlock
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
? "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
|
||||||
}}
|
: "¡Tu pedido se ha realizado con éxito! Gracias por confiar en nosotros, en breves recibirás un correo con la confirmación de tu pedido."
|
||||||
|
|
||||||
{{
|
|
||||||
!checkoutBlock &&
|
|
||||||
"¡Tu pedido se ha realizado con éxito! Gracias por confiar en nosotros, en breves recibirás un correo con la confirmación de tu pedido."
|
|
||||||
}}
|
}}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
@ -260,7 +179,6 @@ export default defineComponent({
|
||||||
<div class="checkout-content">
|
<div class="checkout-content">
|
||||||
<div class="checkout-form">
|
<div class="checkout-form">
|
||||||
<q-form
|
<q-form
|
||||||
action=""
|
|
||||||
method="post"
|
method="post"
|
||||||
id="checkout-form"
|
id="checkout-form"
|
||||||
@submit.prevent="onSubmit"
|
@submit.prevent="onSubmit"
|
||||||
|
@ -477,12 +395,12 @@ export default defineComponent({
|
||||||
<ul class="checkout-summary-list">
|
<ul class="checkout-summary-list">
|
||||||
<li
|
<li
|
||||||
class="checkout-summary-item"
|
class="checkout-summary-item"
|
||||||
v-for="({ title, price, quantity }, index) in cartList"
|
v-for="({ name, price }, index) in cart"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
{{ title }} ({{ quantity }})
|
{{ name }}
|
||||||
<span>{{ price }}</span>
|
<span>{{ price }}€</span>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -545,47 +463,49 @@ export default defineComponent({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="!checkoutBlock" class="checkout-success" id="success-block">
|
<template v-else>
|
||||||
<h6 class="checkout-success-title green-text">
|
<div class="checkout-success" id="success-block">
|
||||||
Has efectuado la siguiente compra
|
<h6 class="checkout-success-title green-text">
|
||||||
</h6>
|
Has efectuado la siguiente compra
|
||||||
|
</h6>
|
||||||
|
|
||||||
<div class="checkout-success-body">
|
<div class="checkout-success-body">
|
||||||
<div class="checkout-success-content">
|
<div class="checkout-success-content">
|
||||||
<ul class="checkout-success-list">
|
<ul class="checkout-success-list">
|
||||||
<li
|
<li
|
||||||
v-for="({ title, price, quantity }, index) in cartList"
|
v-for="({ name, price, image }, index) in cart"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="checkout-success-item"
|
class="checkout-success-item"
|
||||||
>
|
>
|
||||||
<div class="checkout-item-content">
|
<div class="checkout-item-content">
|
||||||
<div class="checkout-product-details">
|
<div class="checkout-product-details">
|
||||||
<img
|
<img
|
||||||
src="../assets/checkout-flower.png"
|
:src="isError ? '../assets/empty-img.jpg' : image"
|
||||||
alt="product"
|
:alt="name"
|
||||||
class="checkout-product-img"
|
class="checkout-product-img"
|
||||||
/>
|
@error="onError"
|
||||||
<p class="checkout-product-title">
|
/>
|
||||||
{{ title }} ({{ quantity }})
|
|
||||||
</p>
|
<p class="checkout-product-title">
|
||||||
|
{{ name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="checkout-product-price">{{ price }}€</p>
|
||||||
</div>
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="checkout-product-price">
|
<footer class="checkout-success-footer">
|
||||||
{{ price }}
|
<p class="checkout-success-paragraph">Total</p>
|
||||||
</p>
|
<p class="checkout-success-paragraph">
|
||||||
</div>
|
{{ totalPrice?.toFixed(2) }}€
|
||||||
</li>
|
</p>
|
||||||
</ul>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="checkout-success-footer">
|
|
||||||
<p class="checkout-success-paragraph">Total</p>
|
|
||||||
<p class="checkout-success-paragraph">
|
|
||||||
{{ totalPrice?.toFixed(2) }}€
|
|
||||||
</p>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
|
|
@ -9,11 +9,8 @@ export default defineComponent({
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
// Cria um intervalo que executa a cada segundo
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
// Decrementa o valor de count
|
|
||||||
counter.value--;
|
counter.value--;
|
||||||
// Se o valor de count for zero, para o intervalo
|
|
||||||
if (counter.value === 1) {
|
if (counter.value === 1) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
push("/");
|
push("/");
|
||||||
|
@ -21,7 +18,6 @@ export default defineComponent({
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chama a função para iniciar o contador quando o componente for montado
|
|
||||||
onMounted(startCountdown);
|
onMounted(startCountdown);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -21,9 +21,10 @@ export default defineComponent({
|
||||||
const mobileStore = useMobileStore();
|
const mobileStore = useMobileStore();
|
||||||
const { isCarouselVisible, isOpenNav, screenWidth } =
|
const { isCarouselVisible, isOpenNav, screenWidth } =
|
||||||
storeToRefs(mobileStore);
|
storeToRefs(mobileStore);
|
||||||
|
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const { getProducts } = cartStore;
|
const { getProducts } = cartStore;
|
||||||
const { products } = storeToRefs(cartStore);
|
const { products, homeSection } = storeToRefs(cartStore);
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
await getProducts();
|
await getProducts();
|
||||||
|
@ -41,6 +42,7 @@ export default defineComponent({
|
||||||
isCarouselVisible,
|
isCarouselVisible,
|
||||||
slidesContent,
|
slidesContent,
|
||||||
screenWidth,
|
screenWidth,
|
||||||
|
homeSection,
|
||||||
isOpenNav,
|
isOpenNav,
|
||||||
products,
|
products,
|
||||||
};
|
};
|
||||||
|
@ -54,35 +56,29 @@ export default defineComponent({
|
||||||
<VerticalCarouselImgs :imgsArr="slidesContent" class="home-carousel" />
|
<VerticalCarouselImgs :imgsArr="slidesContent" class="home-carousel" />
|
||||||
</q-no-ssr>
|
</q-no-ssr>
|
||||||
|
|
||||||
<section class="products-section">
|
<section class="products-section" ref="homeSection">
|
||||||
<header class="products-section-header section-header">
|
<header class="products-section-header section-header">
|
||||||
<h3 class="products-header-title subtitle">
|
<h3 class="products-header-title subtitle">
|
||||||
Diseños de ramos más vendidos
|
Diseños de ramos más vendidos
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="products-header-paragraph section-paragraph">
|
<p class="products-header-paragraph section-paragraph">
|
||||||
Descripción SEO: Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
||||||
elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
|
||||||
aliqua.
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="products-body">
|
<div class="products-body">
|
||||||
<Container cardContainer>
|
<Container cardContainer>
|
||||||
<template
|
<template
|
||||||
v-for="(
|
v-for="({ id, name, price, image, isNew }, i) in products?.data"
|
||||||
{ id, slug, name, price, images, isNew, discount }, i
|
:key="id"
|
||||||
) in products.data.products"
|
|
||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
v-if="i < 8"
|
v-if="i < 8"
|
||||||
:id="slug"
|
:id="id"
|
||||||
:price="price"
|
:price="price"
|
||||||
:title="name"
|
:title="name"
|
||||||
:discount="discount"
|
:imgSrc="image"
|
||||||
:imgSrc="images[0]"
|
|
||||||
:isNew="isNew"
|
:isNew="isNew"
|
||||||
:key="id"
|
|
||||||
imgClass="list-products"
|
imgClass="list-products"
|
||||||
size="md-card"
|
size="md-card"
|
||||||
/>
|
/>
|
||||||
|
@ -102,9 +98,6 @@ export default defineComponent({
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="products-selection-paragraph section-paragraph">
|
<p class="products-selection-paragraph section-paragraph">
|
||||||
Descripción SEO: Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
||||||
elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
|
||||||
aliqua.
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
@ -112,19 +105,15 @@ export default defineComponent({
|
||||||
<q-no-ssr>
|
<q-no-ssr>
|
||||||
<Swiper>
|
<Swiper>
|
||||||
<template
|
<template
|
||||||
v-for="(
|
v-for="({ id, isNew, price, name, image }, i) in products.data"
|
||||||
{ slug, discount, isNew, price, name, images }, i
|
:key="id"
|
||||||
) in products.data.products"
|
|
||||||
:key="slug"
|
|
||||||
>
|
>
|
||||||
<swiper-slide class="swiper-slide" v-if="i < 10">
|
<swiper-slide class="swiper-slide" v-if="i < 10">
|
||||||
<Card
|
<Card
|
||||||
:id="slug"
|
:id="id"
|
||||||
:key="slug"
|
|
||||||
:price="price"
|
:price="price"
|
||||||
:title="name"
|
:title="name"
|
||||||
:discount="discount"
|
:imgSrc="image"
|
||||||
:imgSrc="images[0]"
|
|
||||||
:isNew="isNew"
|
:isNew="isNew"
|
||||||
imgClass="carousel"
|
imgClass="carousel"
|
||||||
size="lg-card"
|
size="lg-card"
|
||||||
|
@ -177,6 +166,7 @@ export default defineComponent({
|
||||||
.products-section {
|
.products-section {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 87px;
|
margin-bottom: 87px;
|
||||||
|
scroll-margin: 30px;
|
||||||
|
|
||||||
& .products-section-header {
|
& .products-section-header {
|
||||||
& .products-header-title {
|
& .products-header-title {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import { useMeta } from "quasar";
|
import { useMeta } from "quasar";
|
||||||
import { useForm } from "vee-validate";
|
import { defineComponent, onBeforeMount, ref, watch } from "vue";
|
||||||
import { defineComponent, onBeforeMount, reactive, ref, watch } from "vue";
|
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
import IconArrowCircleFilledLeft from "components/icons/IconArrowCircleFilledLeft.vue";
|
import IconArrowCircleFilledLeft from "components/icons/IconArrowCircleFilledLeft.vue";
|
||||||
|
@ -19,7 +18,7 @@ import Card from "components/ui/Card.vue";
|
||||||
import Container from "components/ui/Container.vue";
|
import Container from "components/ui/Container.vue";
|
||||||
import Modal from "components/ui/Modal.vue";
|
import Modal from "components/ui/Modal.vue";
|
||||||
|
|
||||||
import { dedicationSchema } from "src/utils/zod/schemas";
|
import { usePostalCalendar } from "src/hooks/usePostalCalendar";
|
||||||
import { useCartStore } from "stores/cart";
|
import { useCartStore } from "stores/cart";
|
||||||
import { useModalStore } from "stores/modalStore";
|
import { useModalStore } from "stores/modalStore";
|
||||||
|
|
||||||
|
@ -43,48 +42,30 @@ export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const {
|
||||||
|
fields: { dedication, dedicationAttrs },
|
||||||
|
} = usePostalCalendar({ modalItem: "isOpenAvailability" });
|
||||||
|
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
const { openModal } = modalStore;
|
const { openModal } = modalStore;
|
||||||
|
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const { getProduct, getProducts, products } = cartStore;
|
const { getProduct, getProducts } = cartStore;
|
||||||
const { prevProduct, currentProduct, nextProduct, addCartLoadingBtn } =
|
const { products, featuredProducts, addCartLoadingBtn } =
|
||||||
storeToRefs(cartStore);
|
storeToRefs(cartStore);
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getProduct(route.params.id);
|
getProduct(route.params.id);
|
||||||
getProducts();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(currentProduct.value, (newValue) => {
|
watch(
|
||||||
useMeta(() => {
|
() => products.value.current?.type,
|
||||||
return {
|
(newCategory) => {
|
||||||
title: `${newValue.value?.title}`,
|
getProducts({
|
||||||
titleTemplate: (title) => `${title} - FloraNet`,
|
// type: newCategory,
|
||||||
meta: {
|
});
|
||||||
description: {
|
}
|
||||||
name: "description",
|
);
|
||||||
content: `${newValue.value?.description}`,
|
|
||||||
},
|
|
||||||
keywords: { name: "keywords", content: `${newValue.value?.title}` },
|
|
||||||
equiv: {
|
|
||||||
"http-equiv": "Content-Type",
|
|
||||||
content: "text/html; charset=UTF-8",
|
|
||||||
},
|
|
||||||
ogTitle: {
|
|
||||||
property: "og:title",
|
|
||||||
template(ogTitle) {
|
|
||||||
return `${ogTitle} - FloraNet`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
noscript: {
|
|
||||||
default:
|
|
||||||
"This is content for browsers with no JS (or disabled JS)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
|
@ -93,45 +74,55 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentData = reactive({});
|
useMeta(() => ({
|
||||||
watch(currentProduct.value, (newData) => {
|
title: `${products.value.current?.title}`,
|
||||||
if (newData.value) {
|
titleTemplate: (title) => `${title} - FloraNet`,
|
||||||
const { id, ...newDataWhithoutId } = newData.value;
|
meta: {
|
||||||
currentData.value = {
|
description: {
|
||||||
...newDataWhithoutId,
|
name: "description",
|
||||||
productId: +route.params.id,
|
content: `${products.value.current?.description}`,
|
||||||
};
|
},
|
||||||
|
keywords: {
|
||||||
|
name: "keywords",
|
||||||
|
content: `${products.value.current?.title}`,
|
||||||
|
},
|
||||||
|
equiv: {
|
||||||
|
"http-equiv": "Content-Type",
|
||||||
|
content: "text/html; charset=UTF-8",
|
||||||
|
},
|
||||||
|
ogTitle: {
|
||||||
|
property: "og:title",
|
||||||
|
template(ogTitle) {
|
||||||
|
return `${ogTitle} - FloraNet`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noscript: {
|
||||||
|
default: "This is content for browsers with no JS (or disabled JS)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const checkImageValidity = (imageLink) => {
|
||||||
|
const validExtensions = [".jpg", ".jpeg", ".png"];
|
||||||
|
|
||||||
|
if (imageLink) {
|
||||||
|
const extension = imageLink.substring(imageLink.lastIndexOf("."));
|
||||||
|
return validExtensions.includes(extension.toLowerCase());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const category = reactive({
|
return true;
|
||||||
1: "Planta",
|
};
|
||||||
2: "Ramos",
|
|
||||||
});
|
|
||||||
|
|
||||||
const { handleSubmit, defineField, handleReset } = useForm({
|
|
||||||
validationSchema: dedicationSchema,
|
|
||||||
});
|
|
||||||
const [dedication, dedicationAttrs] = defineField("dedication");
|
|
||||||
/* const onSubmit = handleSubmit(() => {
|
|
||||||
openModal({ modal: "availability" });
|
|
||||||
// addToCart(currentData.value, dedication);
|
|
||||||
// handleReset();
|
|
||||||
}); */
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
openModal,
|
||||||
|
checkImageValidity,
|
||||||
slide: ref(1),
|
slide: ref(1),
|
||||||
fullscreen: ref(false),
|
fullscreen: ref(false),
|
||||||
dedication,
|
dedication,
|
||||||
dedicationAttrs,
|
dedicationAttrs,
|
||||||
products,
|
products,
|
||||||
|
featuredProducts,
|
||||||
addCartLoadingBtn,
|
addCartLoadingBtn,
|
||||||
prevProduct,
|
|
||||||
currentProduct,
|
|
||||||
nextProduct,
|
|
||||||
currentData,
|
|
||||||
category,
|
|
||||||
openModal,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -141,39 +132,33 @@ export default defineComponent({
|
||||||
<q-page>
|
<q-page>
|
||||||
<Container class="product-container" tag="section">
|
<Container class="product-container" tag="section">
|
||||||
<ProductCarousel>
|
<ProductCarousel>
|
||||||
<template v-for="(img, i) in currentProduct?.images" :key="i">
|
<q-carousel-slide
|
||||||
<q-carousel-slide
|
:img-src="
|
||||||
v-if="img"
|
checkImageValidity(products.current?.image)
|
||||||
:img-src="img"
|
? products.current?.image
|
||||||
class="product-gallery-item"
|
: '../assets/empty-img.jpg'
|
||||||
:name="i + 1"
|
"
|
||||||
/>
|
class="product-gallery-item"
|
||||||
|
:name="1"
|
||||||
<q-carousel-slide
|
/>
|
||||||
v-else
|
|
||||||
:img-src="'../assets/empty-img.jpg'"
|
|
||||||
class="product-gallery-item"
|
|
||||||
:name="1"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</ProductCarousel>
|
</ProductCarousel>
|
||||||
|
|
||||||
<div class="product-content">
|
<div class="product-content">
|
||||||
<header class="product-content-header">
|
<header class="product-content-header">
|
||||||
<h3 class="product-content-title subtitle">
|
<h3 class="product-content-title subtitle">
|
||||||
{{ currentProduct?.name }}
|
{{ products.current?.name }}
|
||||||
<q-skeleton type="rect" v-if="!currentProduct?.name" />
|
<q-skeleton type="rect" v-if="!products.current?.name" />
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div class="product-header-block">
|
<div class="product-header-block">
|
||||||
<p class="product-content-paragraph">
|
<p class="product-content-paragraph">
|
||||||
SKU:
|
SKU:
|
||||||
<span class="green-text" style="display: inline-flex">
|
<span class="green-text" style="display: inline-flex">
|
||||||
{{ currentProduct?.slug }}
|
{{ products.current?.id }}
|
||||||
<q-skeleton
|
<q-skeleton
|
||||||
width="100px"
|
width="100px"
|
||||||
type="text"
|
type="text"
|
||||||
v-if="!currentProduct?.slug"
|
v-if="!products.current?.id"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
@ -181,11 +166,11 @@ export default defineComponent({
|
||||||
<p class="product-content-paragraph">
|
<p class="product-content-paragraph">
|
||||||
Categoría:
|
Categoría:
|
||||||
<span class="green-text">
|
<span class="green-text">
|
||||||
{{ category[currentProduct?.category] }}
|
{{ products.current?.type }}
|
||||||
<q-skeleton
|
<q-skeleton
|
||||||
type="text"
|
type="text"
|
||||||
width="50px"
|
width="50px"
|
||||||
v-if="!currentProduct?.category"
|
v-if="!products.current?.type"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
@ -195,19 +180,21 @@ export default defineComponent({
|
||||||
<div class="product-content-body">
|
<div class="product-content-body">
|
||||||
<div class="product-content-paragraphs">
|
<div class="product-content-paragraphs">
|
||||||
<p class="product-price green-text">
|
<p class="product-price green-text">
|
||||||
{{ currentProduct?.price }}
|
{{ products.current?.price }}€
|
||||||
<q-skeleton
|
<q-skeleton
|
||||||
type="text"
|
type="text"
|
||||||
height="90px"
|
height="90px"
|
||||||
width="80px"
|
width="80px"
|
||||||
v-if="!currentProduct?.price"
|
v-if="!products.current?.price"
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
<p class="product-delivery green-text">Envío Gratuito</p>
|
<p class="product-delivery green-text">Envío Gratuito</p>
|
||||||
<p class="product-description">
|
<p class="product-description">
|
||||||
{{ currentProduct?.description }}
|
{{ products.current?.description }}
|
||||||
<q-skeleton type="text" v-if="!currentProduct?.description" />
|
<q-skeleton type="text" v-if="!products.current?.description" />
|
||||||
<q-skeleton type="text" v-if="!currentProduct?.description" />
|
<q-skeleton type="text" v-if="!products.current?.description" />
|
||||||
|
<q-skeleton type="text" v-if="!products.current?.description" />
|
||||||
|
<q-skeleton type="text" v-if="!products.current?.description" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -263,39 +250,32 @@ export default defineComponent({
|
||||||
:style="+$route.params.id === 1 && 'justify-content: flex-end;'"
|
:style="+$route.params.id === 1 && 'justify-content: flex-end;'"
|
||||||
>
|
>
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="+$route.params.id > 1"
|
||||||
color="white"
|
color="white"
|
||||||
class="btn outlined rounded sm-btn product-pag-item product-prev-btn"
|
class="btn outlined rounded sm-btn product-pag-item product-prev-btn"
|
||||||
:to="`${+$route.params.id - 1}`"
|
:to="`${+$route.params.id - 1}`"
|
||||||
v-if="+$route.params.id > 1"
|
@click="products.current.value = undefined"
|
||||||
@click="currentProduct.value = undefined"
|
|
||||||
>
|
>
|
||||||
<IconArrowCircleFilledLeft />
|
<IconArrowCircleFilledLeft />
|
||||||
|
|
||||||
<div class="btn-pag-paragraphs">
|
<div class="btn-pag-paragraphs">
|
||||||
<p class="btn-paragraph-top green-text">Produto anterior</p>
|
<p class="btn-paragraph-top green-text">Produto anterior</p>
|
||||||
<p
|
<p class="product-paragraph-bottom" :title="products.prev?.name">
|
||||||
class="product-paragraph-bottom"
|
{{ products.prev?.name }}
|
||||||
:title="prevProduct.value?.title"
|
|
||||||
>
|
|
||||||
{{ prevProduct.value?.title }}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="products.next?.id"
|
||||||
color="white"
|
color="white"
|
||||||
class="btn outlined rounded sm-btn product-pag-item product-next-btn"
|
class="btn outlined rounded sm-btn product-pag-item product-next-btn"
|
||||||
:to="`${+$route.params.id + 1}`"
|
:to="`${+$route.params.id + 1}`"
|
||||||
v-if="nextProduct.value?.id"
|
|
||||||
@click="currentProduct.value = undefined"
|
|
||||||
>
|
>
|
||||||
<div class="btn-pag-paragraphs">
|
<div class="btn-pag-paragraphs">
|
||||||
<p class="btn-paragraph-top green-text">Siguiente producto</p>
|
<p class="btn-paragraph-top green-text">Siguiente producto</p>
|
||||||
<p
|
<p class="product-paragraph-bottom" :title="products.next?.name">
|
||||||
class="product-paragraph-bottom"
|
{{ products.next?.name }}
|
||||||
:title="nextProduct.value?.title"
|
|
||||||
>
|
|
||||||
{{ nextProduct.value?.title }}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -314,26 +294,24 @@ export default defineComponent({
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="like-another-paragraph">
|
<p class="like-another-paragraph">
|
||||||
Descripción SEO: Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
||||||
elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
|
||||||
aliqua.
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<Container cardContainer class="no-padding">
|
<Container cardContainer class="no-padding">
|
||||||
<template
|
<template
|
||||||
v-for="({ images, discount, isNew, name, price, slug }, i) in products
|
v-for="(
|
||||||
.data.products"
|
{ image, discount, isNew, name, price, id }, i
|
||||||
|
) in products.data"
|
||||||
|
:key="id"
|
||||||
>
|
>
|
||||||
<Card
|
<Card
|
||||||
v-if="i < 4"
|
v-if="i < 4"
|
||||||
:price="price"
|
:price="price"
|
||||||
:title="name"
|
:title="name"
|
||||||
:discount="discount"
|
:discount="discount"
|
||||||
:imgSrc="images[0]"
|
:imgSrc="image"
|
||||||
:isNew="isNew"
|
:isNew="isNew"
|
||||||
:key="slug"
|
:id="id"
|
||||||
:id="slug"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -37,7 +37,7 @@ const routes = [
|
||||||
path: "",
|
path: "",
|
||||||
name: "Checkout",
|
name: "Checkout",
|
||||||
component: () => import("pages/CheckoutPage.vue"),
|
component: () => import("pages/CheckoutPage.vue"),
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,105 +1,98 @@
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { computed, reactive, ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
import { api, apiBack } from "src/boot/axios";
|
import { apiBack } from "src/boot/axios";
|
||||||
import { quasarNotify } from "src/functions/quasarNotify";
|
import { quasarNotify } from "src/functions/quasarNotify";
|
||||||
|
import { useLocalStorage } from "src/hooks/useLocalStorage";
|
||||||
|
|
||||||
export const useCartStore = defineStore("cart", () => {
|
export const useCartStore = defineStore("cart", () => {
|
||||||
const cart = ref([]);
|
|
||||||
const cartList = ref([]);
|
|
||||||
const products = ref({
|
|
||||||
data: {
|
|
||||||
page: undefined,
|
|
||||||
productsPerPage: undefined,
|
|
||||||
products: [],
|
|
||||||
},
|
|
||||||
prev: {},
|
|
||||||
current: {},
|
|
||||||
next: {},
|
|
||||||
});
|
|
||||||
const dedicationTxt = ref("");
|
|
||||||
const prevProduct = reactive({});
|
|
||||||
const currentProduct = ref();
|
|
||||||
const nextProduct = reactive({});
|
|
||||||
const addCartLoadingBtn = ref(false);
|
|
||||||
const cartLength = computed(() => cart.value.length);
|
|
||||||
const routeId = ref(null);
|
|
||||||
const totalPrice = computed(() => {
|
|
||||||
return cart.value.reduce((acc, { price }) => {
|
|
||||||
if (price) {
|
|
||||||
const priceWithoutLetter = price?.replace("€", "");
|
|
||||||
return +priceWithoutLetter + acc;
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
|
const { addItem, getItem, removeItem } = useLocalStorage()
|
||||||
|
|
||||||
|
//! Elements
|
||||||
|
const checkoutRef = ref(null);
|
||||||
|
const homeSection = ref(null);
|
||||||
|
|
||||||
|
const initialValues = [{
|
||||||
|
id: null,
|
||||||
|
name: "",
|
||||||
|
price: null,
|
||||||
|
image: "",
|
||||||
|
description: "",
|
||||||
|
dateExpired: "",
|
||||||
|
isNew: null,
|
||||||
|
type: "",
|
||||||
|
postalCode: "",
|
||||||
|
order_position: null,
|
||||||
|
recommend: null
|
||||||
|
}]
|
||||||
|
|
||||||
|
//! Variables
|
||||||
|
const cart = ref([]);
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
cart.value = getItem('cart');
|
||||||
|
})()
|
||||||
|
|
||||||
|
const addCartLoadingBtn = ref(false);
|
||||||
|
const routeId = ref(null);
|
||||||
|
const products = ref({
|
||||||
|
data: initialValues,
|
||||||
|
prev: initialValues,
|
||||||
|
current: initialValues,
|
||||||
|
next: initialValues,
|
||||||
|
});
|
||||||
|
const featuredProducts = ref({
|
||||||
|
page: undefined,
|
||||||
|
productsPerPage: undefined,
|
||||||
|
products: [],
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Transforms options object into params object.
|
||||||
*
|
*
|
||||||
* @param debug Allow the data console - boolean
|
* @param {Object} options - The options object.
|
||||||
*
|
* @param {number} options.itens - The items array.
|
||||||
|
* @param {boolean} options.featured - The featured flag.
|
||||||
|
* @param {number} options.page - The page number.
|
||||||
|
* @param {string} options.type - The type name.
|
||||||
|
* @param {string} options.postalCode - The postal code.
|
||||||
|
* @param {string} options.dateExpired - The expiration date.
|
||||||
|
* @param {number} options.minPrice - The minimum price.
|
||||||
|
* @param {number} options.maxPrice - The maximum price.
|
||||||
|
* @param {number} options.bigPrice - The big price.
|
||||||
|
* @param {number} options.lowPrice - The low price.
|
||||||
|
* @param {boolean} options.isNew - The new flag.
|
||||||
|
* @returns {Object} - The params object.
|
||||||
*/
|
*/
|
||||||
async function getCart({ debug }) {
|
function transformOptionsToParams(
|
||||||
try {
|
|
||||||
const { data } = await api.get("cart");
|
|
||||||
const cartItems = data.reduce((obj, { title, price, ...rest }) => {
|
|
||||||
const priceWithoutLetter = +price.replace("€", "");
|
|
||||||
|
|
||||||
if (obj[title]) {
|
|
||||||
obj[title].quantity++;
|
|
||||||
} else {
|
|
||||||
obj[title] = {
|
|
||||||
title,
|
|
||||||
price: `${priceWithoutLetter}€`,
|
|
||||||
quantity: 1,
|
|
||||||
...rest,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
cartList.value = Object.values(cartItems);
|
|
||||||
cart.value = data;
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
console.groupCollapsed("%c Cart is fetched!", "color: green;");
|
|
||||||
console.table(cart.value);
|
|
||||||
console.groupEnd();
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
new Error(`FATAL ERROR ::: ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getCart({ debug: true });
|
|
||||||
|
|
||||||
async function getProducts(
|
|
||||||
options = {
|
options = {
|
||||||
itens: undefined,
|
|
||||||
featured: undefined,
|
|
||||||
page: undefined,
|
|
||||||
category: undefined,
|
|
||||||
postalCode: undefined,
|
postalCode: undefined,
|
||||||
dateExpired: undefined,
|
dateExpired: undefined,
|
||||||
|
type: undefined,
|
||||||
minPrice: undefined,
|
minPrice: undefined,
|
||||||
maxPrice: undefined,
|
maxPrice: undefined,
|
||||||
bigPrice: undefined,
|
bigPrice: undefined,
|
||||||
lowPrice: undefined,
|
lowPrice: undefined,
|
||||||
isNew: undefined,
|
isNew: undefined,
|
||||||
},
|
order_crescent: undefined,
|
||||||
navigate
|
order_descending: undefined,
|
||||||
|
recommend: undefined,
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
const optionsObj = {
|
const optionsObj = {
|
||||||
itens: options.itens,
|
|
||||||
featured: options.featured,
|
|
||||||
page: options.page,
|
|
||||||
category: options.category,
|
|
||||||
postalCode: options.postalCode,
|
postalCode: options.postalCode,
|
||||||
dateExpired: options.dateExpired,
|
dateExpired: options.dateExpired,
|
||||||
|
type: options.type,
|
||||||
minPrice: options.minPrice,
|
minPrice: options.minPrice,
|
||||||
maxPrice: options.maxPrice,
|
maxPrice: options.maxPrice,
|
||||||
bigPrice: options.bigPrice,
|
bigPrice: options.bigPrice,
|
||||||
lowPrice: options.lowPrice,
|
lowPrice: options.lowPrice,
|
||||||
isNew: options.isNew,
|
isNew: options.isNew,
|
||||||
|
order_crescent: options.order_crescent,
|
||||||
|
order_descending: options.order_descending,
|
||||||
|
recommend: options.recommend,
|
||||||
};
|
};
|
||||||
const validKeys = Object.keys(options).filter(
|
const validKeys = Object.keys(options).filter(
|
||||||
(key) => options[key] !== undefined
|
(key) => options[key] !== undefined
|
||||||
|
@ -109,14 +102,51 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
try {
|
return params;
|
||||||
const {
|
}
|
||||||
data: { data },
|
|
||||||
} = await apiBack.get("products", {
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data[0].products.length === 0) {
|
/**
|
||||||
|
* Fetches products based on the provided options.
|
||||||
|
*
|
||||||
|
* @param {Object} options - The options for fetching products.
|
||||||
|
* @param {number} options.itens - The items to fetch.
|
||||||
|
* @param {boolean} options.featured - Whether to fetch only featured products.
|
||||||
|
* @param {number} options.page - The page number to fetch.
|
||||||
|
* @param {string} options.type - The type of products to fetch.
|
||||||
|
* @param {string} options.postalCode - The postal code for filtering products.
|
||||||
|
* @param {string} options.dateExpired - The expiration date for filtering products.
|
||||||
|
* @param {number} options.minPrice - The minimum price for filtering products.
|
||||||
|
* @param {number} options.maxPrice - The maximum price for filtering products.
|
||||||
|
* @param {number} options.bigPrice - The big price for filtering products.
|
||||||
|
* @param {number} options.lowPrice - The low price for filtering products.
|
||||||
|
* @param {boolean} options.isNew - Whether to fetch only new products.
|
||||||
|
* @param {Function} navigate - The navigation function to call after fetching products.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the products are fetched.
|
||||||
|
*/
|
||||||
|
async function getProducts(
|
||||||
|
options = {
|
||||||
|
postalCode: undefined,
|
||||||
|
dateExpired: undefined,
|
||||||
|
type: undefined,
|
||||||
|
minPrice: undefined,
|
||||||
|
maxPrice: undefined,
|
||||||
|
bigPrice: undefined,
|
||||||
|
lowPrice: undefined,
|
||||||
|
isNew: undefined,
|
||||||
|
order_crescent: undefined,
|
||||||
|
order_descending: undefined,
|
||||||
|
recommend: undefined,
|
||||||
|
},
|
||||||
|
scrollIntoView = () => { }
|
||||||
|
) {
|
||||||
|
const params = transformOptionsToParams(options);
|
||||||
|
console.log(params);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data: { data } } = await apiBack.get("products", { params });
|
||||||
|
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
return quasarNotify({
|
return quasarNotify({
|
||||||
message:
|
message:
|
||||||
"No hay productos disponibles para la fecha y el código postal seleccionados",
|
"No hay productos disponibles para la fecha y el código postal seleccionados",
|
||||||
|
@ -124,22 +154,12 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
products.value.data = data[0];
|
products.value.data = data;
|
||||||
|
|
||||||
if (navigate) {
|
if (scrollIntoView) {
|
||||||
navigate();
|
scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentProductIndex = data.findIndex(
|
|
||||||
({ slug }) => slug === routeId.value
|
|
||||||
);
|
|
||||||
const prevProductIndex = currentProductIndex - 1;
|
|
||||||
const nextProductIndex = currentProductIndex + 1;
|
|
||||||
|
|
||||||
products.value.prev = data.data[prevProductIndex];
|
|
||||||
products.value.current = data.data[currentProductIndex];
|
|
||||||
products.value.next = data.data[nextProductIndex];
|
|
||||||
|
|
||||||
console.groupCollapsed("%c PRODUCTS FETCHED!", "color: green;");
|
console.groupCollapsed("%c PRODUCTS FETCHED!", "color: green;");
|
||||||
console.groupCollapsed("%c PRODUCTS DATA", "color: tomato;");
|
console.groupCollapsed("%c PRODUCTS DATA", "color: tomato;");
|
||||||
console.table(products.value.data);
|
console.table(products.value.data);
|
||||||
|
@ -147,7 +167,10 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
console.groupCollapsed("%c PREV PRODUCT", "color: tomato;");
|
console.groupCollapsed("%c PREV PRODUCT", "color: tomato;");
|
||||||
console.table(products.value.prev);
|
console.table(products.value.prev);
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
console.groupCollapsed("%c CURRENT PRODUCT", "color: tomato;");
|
console.groupCollapsed(
|
||||||
|
`%c CURRENT PRODUCT: ${products.value.current.slug}`,
|
||||||
|
"color: tomato;"
|
||||||
|
);
|
||||||
console.table(products.value.current);
|
console.table(products.value.current);
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
console.groupCollapsed("%c NEXT PRODUCT", "color: tomato;");
|
console.groupCollapsed("%c NEXT PRODUCT", "color: tomato;");
|
||||||
|
@ -160,18 +183,34 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Fetches a product by its ID and updates the cart state.
|
||||||
*
|
*
|
||||||
* @param id Id to get product
|
* @param {string} id - The ID of the product to fetch.
|
||||||
*
|
* @param {object} options - Additional options for the product fetch.
|
||||||
|
* @param {string} options.type - The type of the product.
|
||||||
|
* @param {string} options.postalCode - The postal code for location-based filtering.
|
||||||
|
* @param {string} options.dateExpired - The expiration date for time-based filtering.
|
||||||
|
* @param {boolean} debug - Flag indicating whether to enable debug mode.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the product is fetched and the cart state is updated.
|
||||||
*/
|
*/
|
||||||
async function getProduct(id) {
|
async function getProduct(
|
||||||
|
id,
|
||||||
|
options = {
|
||||||
|
type: undefined,
|
||||||
|
postalCode: undefined,
|
||||||
|
dateExpired: undefined,
|
||||||
|
},
|
||||||
|
debug = false
|
||||||
|
) {
|
||||||
if (id) {
|
if (id) {
|
||||||
routeId.value = id;
|
routeId.value = id;
|
||||||
try {
|
try {
|
||||||
/* const promises = [
|
const params = transformOptionsToParams(options);
|
||||||
api.get(`flowers/${id - 1}`),
|
|
||||||
api.get(`flowers/${id}`),
|
const promises = [
|
||||||
api.get(`flowers/${id + 1}`),
|
apiBack.get(`products/${+id - 1}`),
|
||||||
|
apiBack.get(`products/${+id}`),
|
||||||
|
apiBack.get(`products/${+id + 1}`),
|
||||||
];
|
];
|
||||||
const results = await Promise.allSettled(promises);
|
const results = await Promise.allSettled(promises);
|
||||||
const [prev, current, next] = results.map((res) => {
|
const [prev, current, next] = results.map((res) => {
|
||||||
|
@ -180,27 +219,29 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
rejected: res.reason,
|
rejected: res.reason,
|
||||||
};
|
};
|
||||||
|
|
||||||
return result[res.status];
|
return result[res.status].data[0];
|
||||||
}); */
|
})
|
||||||
|
|
||||||
const { data } = await apiBack.get(`products/slug/${id}`);
|
products.value.prev = prev;
|
||||||
|
products.value.current = current;
|
||||||
|
products.value.next = next;
|
||||||
|
|
||||||
prevProduct.value = {};
|
if (!current) {
|
||||||
currentProduct.value = data.data[0];
|
push({ name: "NotFound" })
|
||||||
nextProduct.value = {};
|
}
|
||||||
|
|
||||||
console.groupCollapsed(
|
if (debug) {
|
||||||
`%c PRODUCT FETCHED! SLUG: ${routeId.value}`,
|
console.groupCollapsed(
|
||||||
"color: green;"
|
`%c PRODUCT FETCHED! SLUG: ${routeId.value}`,
|
||||||
);
|
"color: green;"
|
||||||
console.time();
|
);
|
||||||
console.table(prevProduct.value);
|
console.table(products.value.prev);
|
||||||
console.table(currentProduct.value);
|
console.table(products.value.current);
|
||||||
console.table(nextProduct.value);
|
console.table(products.value.next);
|
||||||
console.timeEnd();
|
console.groupEnd();
|
||||||
console.groupEnd();
|
}
|
||||||
|
|
||||||
if (currentProduct.value.response?.status === 404) {
|
if (products.value.current.response?.status === 404) {
|
||||||
push({ name: "NotFound" });
|
push({ name: "NotFound" });
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -209,46 +250,107 @@ export const useCartStore = defineStore("cart", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addToCart(product, dedication) {
|
/**
|
||||||
cart.value.push({ ...product.value });
|
* Retrieves featured products based on the provided options.
|
||||||
dedicationTxt.value = dedication;
|
*
|
||||||
addCartLoadingBtn.value = true;
|
* @param {Object} options - The options for retrieving featured products.
|
||||||
|
* @param {number} options.itens - The number of items to retrieve.
|
||||||
|
* @param {number} options.featured - The flag indicating if the products should be featured.
|
||||||
|
* @param {number} [options.page] - The page number for pagination.
|
||||||
|
* @param {string} [options.type] - The type of the products.
|
||||||
|
* @param {string} [options.postalCode] - The postal code for location-based filtering.
|
||||||
|
* @param {string} [options.dateExpired] - The expiration date for filtering.
|
||||||
|
* @param {number} [options.minPrice] - The minimum price for filtering.
|
||||||
|
* @param {number} [options.maxPrice] - The maximum price for filtering.
|
||||||
|
* @param {number} [options.bigPrice] - The big price for filtering.
|
||||||
|
* @param {number} [options.lowPrice] - The low price for filtering.
|
||||||
|
* @param {boolean} [options.isNew] - The flag indicating if the products are new.
|
||||||
|
* @returns {Promise<void>} - A promise that resolves when the featured products are retrieved.
|
||||||
|
*/
|
||||||
|
async function getFeaturedProducts(
|
||||||
|
options = {
|
||||||
|
postalCode: undefined,
|
||||||
|
dateExpired: undefined,
|
||||||
|
type: undefined,
|
||||||
|
minPrice: undefined,
|
||||||
|
maxPrice: undefined,
|
||||||
|
bigPrice: undefined,
|
||||||
|
lowPrice: undefined,
|
||||||
|
isNew: undefined,
|
||||||
|
order_crescent: undefined,
|
||||||
|
order_descending: undefined,
|
||||||
|
recommend: 1,
|
||||||
|
},
|
||||||
|
debug = false
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await api.post("cart", product);
|
const params = transformOptionsToParams(options);
|
||||||
addCartLoadingBtn.value = false;
|
|
||||||
// push("/checkout");
|
|
||||||
|
|
||||||
console.groupCollapsed("%c Adicionado com sucesso!", "color: green");
|
(async () => {
|
||||||
console.table(cart.value);
|
const {
|
||||||
console.groupEnd();
|
data: { data },
|
||||||
|
} = await apiBack.get("products", { params });
|
||||||
|
featuredProducts.value = data[0];
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
console.groupCollapsed(
|
||||||
|
"%c FEATURED PRODUCTS FETCHED!",
|
||||||
|
"color: green;"
|
||||||
|
);
|
||||||
|
console.table(data.products);
|
||||||
|
console.groupEnd();
|
||||||
|
}
|
||||||
|
})();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addCartLoadingBtn.value = false;
|
|
||||||
new Error(`FATAL ERROR ::: ${err}`);
|
new Error(`FATAL ERROR ::: ${err}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adiciona um produto ao carrinho.
|
||||||
|
* @param {Object} product - O produto a ser adicionado.
|
||||||
|
* @param {string} dedication - A dedicação associada ao produto.
|
||||||
|
*/
|
||||||
|
function addToCart(product, dedication) {
|
||||||
|
const existingProduct = cart.value.find(p => p.id === product.id);
|
||||||
|
console.log(existingProduct)
|
||||||
|
|
||||||
|
if (!existingProduct) {
|
||||||
|
const arr = [...cart.value];
|
||||||
|
arr.push(product);
|
||||||
|
console.log(arr)
|
||||||
|
addItem("cart", JSON.stringify(arr));
|
||||||
|
quasarNotify({ message: 'Producto añadido al carrito.', type: 'success' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
quasarNotify({
|
||||||
|
message: "Este producto ya está en el carrito",
|
||||||
|
type: "info",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the cart by its ID.
|
||||||
|
* @param {number} id - The ID of the item to be removed.
|
||||||
|
*/
|
||||||
function removeFromCart(id) {
|
function removeFromCart(id) {
|
||||||
cart.value = cart.value.filter((p) => id !== p.id);
|
const newArrRemovedItem = cart.value.filter((p) => id !== p.id);
|
||||||
api.delete(`cart/${id}`);
|
addItem("cart", JSON.stringify(newArrRemovedItem))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
checkoutRef,
|
||||||
|
homeSection,
|
||||||
|
|
||||||
cart,
|
cart,
|
||||||
cartList,
|
|
||||||
totalPrice,
|
|
||||||
dedicationTxt,
|
|
||||||
cartLength,
|
|
||||||
prevProduct,
|
|
||||||
currentProduct,
|
|
||||||
nextProduct,
|
|
||||||
addCartLoadingBtn,
|
addCartLoadingBtn,
|
||||||
products,
|
products,
|
||||||
|
featuredProducts,
|
||||||
|
|
||||||
|
getFeaturedProducts,
|
||||||
getProducts,
|
getProducts,
|
||||||
addToCart,
|
addToCart,
|
||||||
removeFromCart,
|
removeFromCart,
|
||||||
getProduct,
|
getProduct,
|
||||||
getCart,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
|
import { fullCurrentDate } from "src/constants/date";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { postalCode } from "..";
|
import { postalCode } from "..";
|
||||||
import * as M from "../messages";
|
import * as M from "../messages";
|
||||||
|
|
||||||
const availabilityObj = {
|
const availabilityObj = {
|
||||||
date: z.string({ required_error: M.requiredMessage }).refine((val) => {
|
date: z.string({ required_error: M.requiredMessage }).refine((val) => {
|
||||||
const [day, month, year] = val.split("/");
|
const regex = /-|\//g;
|
||||||
const regex = /\//g;
|
const [day, month = "", year = ""] = val.split(regex);
|
||||||
const valWithoutSlash = val.replace(regex, "");
|
const valWithoutSlash = val.replace(regex, "");
|
||||||
const data = new Date(`${year}-${month}-${day}`);
|
|
||||||
const today = new Date();
|
|
||||||
|
|
||||||
return valWithoutSlash.length === 8 && data >= today;
|
const inputDate = `${year}/${month}/${day}`;
|
||||||
|
|
||||||
|
return valWithoutSlash.length === 8 && inputDate >= fullCurrentDate;
|
||||||
}, "La fecha no puede ser inferior al día de hoy!"),
|
}, "La fecha no puede ser inferior al día de hoy!"),
|
||||||
postalCode,
|
postalCode,
|
||||||
|
dedication: z.string().optional(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const availabilitySchema = z.object(availabilityObj);
|
export const availabilitySchema = z.object(availabilityObj);
|
||||||
|
|
Loading…
Reference in New Issue