Calls a bbdd para cargar los productos externos desde api, prebas stripe

This commit is contained in:
Jaume Solís 2024-01-17 18:15:19 +01:00
parent 715e9f0ca0
commit b1570a2978
39 changed files with 2752 additions and 1973 deletions

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ yarn-error.log*
# local .env files
.env.local*
api/.env

View File

@ -74,5 +74,8 @@
"workbench.tree.indent": 16,
"window.zoomLevel": 0,
"git.ignoreRebaseWarning": true,
"editor.largeFileOptimizations": false
"editor.largeFileOptimizations": false,
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}

5
api/.env.example Normal file
View File

@ -0,0 +1,5 @@
HOST="127.0.0.1"
DB_USER="root"
DB_PASSWORD="root"
PORT ="3306"
DATABASE="floranet"

View File

@ -1,46 +1,117 @@
const db = require("../db/db");
const productsJson = require("./products.json")
class ProductController {
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.size,
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;
let productsFilter = productsJson
let productsFilter = mapedProducts
console.log(params);
if (Number(params.featured)) {
productsFilter = productsFilter.filter(item => item.featured === Number(params.featured))
}
/*let productsFilterPages = []
const totalItens = params?.itens ? Number(params.itens) : 200
const productFilterItens = params?.itens ? products.slice(0, totalItens) : products
if (Number(params.itensPerPage) > 1) {
const itensPerPage = Number(params.itensPerPage)
const totalPages = Math.ceil(productFilterItens.length / itensPerPage)
for (let i = 1; i <= totalPages; i++) {
const startIndex = (i - 1) * itensPerPage;
const endIndex = i * itensPerPage;
const lastestIndex = endIndex > totalItens ? totalItens : endIndex
productsFilterPages.push({
page: i,
itensPerProduct: lastestIndex - startIndex,
products: productFilterItens.slice(startIndex, lastestIndex)
})
}
if (params.category) {
productsFilter = productsFilter.filter(item => item.category === Number(params.category))
}
else {
productsFilterPages.push({
page: 1,
itensPerProduct: totalItens,
products: productFilterItens
if (params.postalCode) {
productsFilter = productsFilter.filter(item => item.postalCode === params.postalCode)
}
if (params.minPrice && !params.maxPrice) {
productsFilter = productsFilter.filter(item => {
const price = Number(item.price.replace(/€/g, ''))
if (price >= Number(params.minPrice)) {
return item
}
})
} */
}
if (params.maxPrice && !params.minPrice) {
productsFilter = productsFilter.filter(item => {
const price = Number(item.price.replace(/€/g, ''))
if (price <= Number(params.maxPrice)) {
return item
}
})
}
if (params.maxPrice && params.minPrice) {
productsFilter = productsFilter.filter(item => {
const price = Number(item.price.replace(/€/g, ''))
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
}
})
}
if (Number(params.bigPrice)) {
productsFilter.sort((a, b) => {
const itemA = Number(a.price.replace(/€/g, ''))
const itemB = Number(b.price.replace(/€/g, ''))
return itemB - itemA;
})
}
if (Number(params.lowPrice)) {
productsFilter.sort((a, b) => {
const itemA = Number(a.price.replace(/€/g, ''))
const itemB = Number(b.price.replace(/€/g, ''))
return itemA - itemB;
})
}
if (params.isNew) {
productsFilter = productsFilter.filter(item => item.isNew === true)
}
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({
data: productsFilter
data: productsFilterPages
})
}

File diff suppressed because it is too large Load Diff

28
api/db/db.js Normal file
View File

@ -0,0 +1,28 @@
async function connect() {
if (global.connection && global.connection.state !== 'disconnected')
return global.connection;
const host = process.env.HOST;
const port = process.env.PORT;
const database = process.env.DATABASE;
const user = process.env.DB_USER;
const password = process.env.DB_PASSWORD;
const mysql = require("mysql2/promise");
const connection = await mysql.createConnection("mysql://" + user + ":" + password + "@" + host + ":" + port + "/" + database + "");
console.log("Conectou no MySQL!");
global.connection = connection;
return connection;
}
async function getProducts() {
const conn = await connect();
const [rows] = await conn.query('CALL getProduct("2024-01-30", "08001")');
return rows;
}
module.exports = { getProducts }

300
api/package-lock.json generated
View File

@ -1,127 +1,17 @@
{
"name": "api",
"name": "backend",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "api",
"name": "backend",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@faker-js/faker": "^8.3.1",
"class-validator": "^0.14.0",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"reflect-metadata": "^0.2.1"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/node": "^20.10.7",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
"express": "^4.18.2"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@faker-js/faker": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.3.1.tgz",
"integrity": "sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/fakerjs"
}
],
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
"npm": ">=6.14.13"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
},
"node_modules/@types/cors": {
"version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "20.10.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz",
"integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/validator": {
"version": "13.11.7",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.7.tgz",
"integrity": "sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q=="
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -134,33 +24,6 @@
"node": ">= 0.6"
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-walk": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz",
"integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@ -210,16 +73,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/class-validator": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz",
"integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==",
"dependencies": {
"@types/validator": "^13.7.10",
"libphonenumber-js": "^1.10.14",
"validator": "^13.7.0"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@ -252,24 +105,6 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -308,26 +143,6 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/dotenv": {
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -544,17 +359,6 @@
"node": ">= 0.10"
}
},
"node_modules/libphonenumber-js": {
"version": "1.10.53",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz",
"integrity": "sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw=="
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -619,14 +423,6 @@
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
@ -707,11 +503,6 @@
"node": ">= 0.8"
}
},
"node_modules/reflect-metadata": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
"integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw=="
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -826,49 +617,6 @@
"node": ">=0.6"
}
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@ -881,25 +629,6 @@
"node": ">= 0.6"
}
},
"node_modules/typescript": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@ -916,20 +645,6 @@
"node": ">= 0.4.0"
}
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/validator": {
"version": "13.11.0",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz",
"integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -937,15 +652,6 @@
"engines": {
"node": ">= 0.8"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
}
}
}

View File

@ -51,6 +51,7 @@
src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-element-bundle.min.js"
defer
></script>
<script src="https://js.stripe.com/v3" defer></script>
</head>
<body>
<!-- quasar:entry-point -->

193
package-lock.json generated
View File

@ -13,6 +13,8 @@
"@vue-stripe/vue-stripe": "^4.5.0",
"@vueuse/core": "^10.7.0",
"axios": "^1.2.1",
"express": "^4.18.2",
"mysql2": "^3.7.0",
"pinia": "^2.0.11",
"quasar": "^2.6.0",
"vee-validate": "^4.12.2",
@ -888,7 +890,6 @@
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dev": true,
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
@ -1085,8 +1086,7 @@
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"dev": true
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/astral-regex": {
"version": "2.0.0",
@ -1217,7 +1217,6 @@
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
@ -1241,7 +1240,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -1250,7 +1248,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"dependencies": {
"ms": "2.0.0"
}
@ -1258,8 +1255,7 @@
"node_modules/body-parser/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/boolbase": {
"version": "1.0.0",
@ -1367,7 +1363,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
@ -1693,7 +1688,6 @@
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dev": true,
"dependencies": {
"safe-buffer": "5.2.1"
},
@ -1705,7 +1699,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -1725,7 +1718,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -1734,7 +1726,6 @@
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -1742,8 +1733,7 @@
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"dev": true
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/core-util-is": {
"version": "1.0.3",
@ -1865,7 +1855,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
@ -1892,11 +1881,18 @@
"node": ">=0.4.0"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -1905,7 +1901,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"dev": true,
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
@ -1950,8 +1945,7 @@
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"dev": true
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
"version": "1.4.625",
@ -1981,7 +1975,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -2386,8 +2379,7 @@
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
@ -2621,7 +2613,6 @@
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -2630,7 +2621,6 @@
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dev": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@ -2697,7 +2687,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"dependencies": {
"ms": "2.0.0"
}
@ -2705,14 +2694,12 @@
"node_modules/express/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express/node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -2837,7 +2824,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dev": true,
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
@ -2855,7 +2841,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"dependencies": {
"ms": "2.0.0"
}
@ -2863,8 +2848,7 @@
"node_modules/finalhandler/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/find-up": {
"version": "5.0.0",
@ -2947,7 +2931,6 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -2969,7 +2952,6 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -3018,11 +3000,18 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"dependencies": {
"is-property": "^1.0.2"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -3036,7 +3025,6 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
@ -3110,7 +3098,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3"
},
@ -3143,7 +3130,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.2.2"
},
@ -3155,7 +3141,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@ -3167,7 +3152,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@ -3179,7 +3163,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@ -3221,7 +3204,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dev": true,
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
@ -3237,7 +3219,6 @@
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
@ -3318,8 +3299,7 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/inquirer": {
"version": "8.2.6",
@ -3351,7 +3331,6 @@
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true,
"engines": {
"node": ">= 0.10"
}
@ -3479,6 +3458,11 @@
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
"dev": true
},
"node_modules/is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
},
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@ -3847,6 +3831,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"node_modules/lowdb": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz",
@ -3902,7 +3891,6 @@
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -3910,8 +3898,7 @@
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
"dev": true
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/merge2": {
"version": "1.4.1",
@ -3956,7 +3943,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -3978,7 +3964,6 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"bin": {
"mime": "cli.js"
},
@ -4090,6 +4075,62 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
"node_modules/mysql2": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.7.0.tgz",
"integrity": "sha512-c45jA3Jc1X8yJKzrWu1GpplBKGwv/wIV6ITZTlCSY7npF2YfJR+6nMP5e+NTQhUeJPSyOQAbGDCGEHbAl8HN9w==",
"dependencies": {
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru-cache": "^8.0.0",
"named-placeholders": "^1.1.3",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/mysql2/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/mysql2/node_modules/lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
"engines": {
"node": ">=16.14"
}
},
"node_modules/named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
"dependencies": {
"lru-cache": "^7.14.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/named-placeholders/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"engines": {
"node": ">=12"
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
@ -4117,7 +4158,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -4180,7 +4220,6 @@
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@ -4189,7 +4228,6 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dev": true,
"dependencies": {
"ee-first": "1.1.1"
},
@ -4351,7 +4389,6 @@
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -4392,8 +4429,7 @@
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"dev": true
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/picocolors": {
"version": "1.0.0",
@ -4569,7 +4605,6 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dev": true,
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
@ -4596,7 +4631,6 @@
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true,
"dependencies": {
"side-channel": "^1.0.4"
},
@ -4654,7 +4688,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -4663,7 +4696,6 @@
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dev": true,
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -4678,7 +4710,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -4936,8 +4967,7 @@
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
"version": "1.69.7",
@ -4987,7 +5017,6 @@
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dev": true,
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@ -5011,7 +5040,6 @@
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"dependencies": {
"ms": "2.0.0"
}
@ -5019,14 +5047,17 @@
"node_modules/send/node_modules/debug/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
@ -5041,7 +5072,6 @@
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dev": true,
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
@ -5062,7 +5092,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"dev": true,
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
@ -5076,8 +5105,7 @@
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"dev": true
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/shallow-clone": {
"version": "3.0.1",
@ -5116,7 +5144,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
@ -5166,6 +5193,14 @@
"node": ">=0.10.0"
}
},
"node_modules/sqlstring": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/stack-trace": {
"version": "1.0.0-pre2",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0-pre2.tgz",
@ -5179,7 +5214,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -5378,7 +5412,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true,
"engines": {
"node": ">=0.6"
}
@ -5459,7 +5492,6 @@
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dev": true,
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
@ -5512,7 +5544,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"dev": true,
"engines": {
"node": ">= 0.8"
}
@ -5572,7 +5603,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"dev": true,
"engines": {
"node": ">= 0.4.0"
}
@ -5587,7 +5617,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"dev": true,
"engines": {
"node": ">= 0.8"
}

View File

@ -9,7 +9,7 @@
"lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"dev": "quasar dev -m ssr",
"api": "cd api && node index.js",
"api": "cd api && node --env-file .env index.js ",
"build": "quasar build -m ssr",
"build:api": "cd api && tsc",
"start:build": "npm run build && cd dist/ssr && npm i && npm run start",
@ -22,6 +22,7 @@
"@vueuse/core": "^10.7.0",
"axios": "^1.2.1",
"express": "^4.18.2",
"mysql2": "^3.7.0",
"pinia": "^2.0.11",
"quasar": "^2.6.0",
"vee-validate": "^4.12.2",

View File

@ -28,7 +28,7 @@ module.exports = configure(function (/* ctx */) {
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: ["i18n", "axios"],
boot: ["i18n", "axios" /* , "stripe" */],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: ["app.scss"],

View File

@ -8,7 +8,7 @@ import { boot } from "quasar/wrappers";
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL: "http://localhost:3000/jsonServer/" });
const apiBack = axios.create({ baseURL: "http://localhost:5000/api/" });
const apiBack = axios.create({ baseURL: "http://localhost:9999/api/" });
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api

17
src/boot/stripe.js Normal file
View File

@ -0,0 +1,17 @@
import { StripePlugin } from "@vue-stripe/vue-stripe";
import { boot } from "quasar/wrappers";
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async ({ app, router, store }) => {
if (typeof window === "undefined") return;
const options = {
pk: process.env.STRIPE_PUBLISHABLE_KEY,
stripeAccount: process.env.STRIPE_ACCOUNT,
apiVersion: process.env.API_VERSION,
locale: process.env.LOCALE,
};
app.use(StripePlugin, options);
});

View File

@ -25,13 +25,15 @@ export default defineComponent({
const validationSchema = toTypedSchema(
availabilitySchema.pick({ date: true })
);
const { errors, defineField } = useForm({
const { errors, defineField, values } = useForm({
validationSchema,
});
const [calendar, calendarAttrs] = defineField("date");
const onBlur = () => {
availability.value.date = calendar.value;
};
availability.value.date = values.date;
watch(errors, (newErrors) => {
if (newErrors.date) {
@ -93,7 +95,7 @@ export default defineComponent({
<div class="custom-block-content">
<p class="custom-head-paragraph">¿Cuándo?</p>
<q-input
<!-- <q-input
class="custom-date-input"
placeholder="Elige una fecha"
v-model="calendar"
@ -102,7 +104,8 @@ export default defineComponent({
@blur="onBlur"
borderless
dense
/>
/> -->
<slot></slot>
</div>
</div>
</template>

View File

@ -0,0 +1,62 @@
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { Field, useForm } from "vee-validate";
import { defineComponent, watch } from "vue";
import { quasarNotify } from "src/functions/quasarNotify";
import { useFormStore } from "src/stores/forms";
import { availabilitySchema } from "src/utils/zod/schemas/availabilitySchema";
import IconPostalCode from "../icons/IconPostalCode.vue";
export default defineComponent({
name: "PostalCodeEx",
components: { IconPostalCode, Field },
setup() {
const formStore = useFormStore();
const validationSchema = toTypedSchema(
availabilitySchema.pick({ date: true })
);
const { errors, values, handleSubmit } = useForm({
validationSchema,
initialValues: {
date: "",
},
});
watch(values, (newValues) => {
formStore.$patch({
availability: {
postalCode: newValues.date,
},
});
});
watch(errors, (newErrors) => {
if (newErrors.date) {
quasarNotify({ message: newErrors.date, type: "erro" });
}
});
const onSubmit = handleSubmit(formStore.registerAvailability);
return {
postalCode,
postalCodeAttrs,
errors,
onBlur,
};
},
});
</script>
<template>
<div class="custom-input-el postal-code">
<IconPostalCode />
<div class="custom-block-content">
<p class="custom-head-paragraph">¿Dónde?</p>
<!-- <p class="custom-main-paragraph">código postal</p> -->
<Field />
</div>
</div>
</template>

View File

@ -0,0 +1,62 @@
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { Field, useForm } from "vee-validate";
import { defineComponent, watch } from "vue";
import { quasarNotify } from "src/functions/quasarNotify";
import { useFormStore } from "src/stores/forms";
import { availabilitySchema } from "src/utils/zod/schemas/availabilitySchema";
import IconPostalCode from "../icons/IconPostalCode.vue";
export default defineComponent({
name: "PostalCodeEx",
components: { IconPostalCode, Field },
setup() {
const formStore = useFormStore();
const validationSchema = toTypedSchema(
availabilitySchema.pick({ postalCode: true })
);
const { errors, values, handleSubmit } = useForm({
validationSchema,
initialValues: {
postalCode: "",
},
});
watch(values, (newValues) => {
formStore.$patch({
availability: {
postalCode: newValues.postalCode,
},
});
});
watch(errors, (newErrors) => {
if (newErrors.postalCode) {
quasarNotify({ message: newErrors.postalCode, type: "erro" });
}
});
const onSubmit = handleSubmit(formStore.registerAvailability);
return {
postalCode,
postalCodeAttrs,
errors,
onBlur,
};
},
});
</script>
<template>
<div class="custom-input-el postal-code">
<IconPostalCode />
<div class="custom-block-content">
<p class="custom-head-paragraph">¿Dónde?</p>
<!-- <p class="custom-main-paragraph">código postal</p> -->
<Field />
</div>
</div>
</template>

View File

@ -18,16 +18,17 @@ export default defineComponent({
const validationSchema = toTypedSchema(
availabilitySchema.pick({ postalCode: true }).partial()
);
const { errors, defineField } = useForm({
const { errors, defineField, values } = useForm({
validationSchema,
initialValues: {
postalCode: availability.value.date,
postalCode: availability.value.postalCode,
},
});
const [postalCode, postalCodeAttrs] = defineField("postalCode");
const onBlur = () => {
availability.value.postalCode = postalCode.value;
};
availability.value.postalCode = values.postalCode;
watch(errors, (newErrors) => {
if (newErrors.postalCode) {
@ -51,8 +52,9 @@ export default defineComponent({
<div class="custom-block-content">
<p class="custom-head-paragraph">¿Dónde?</p>
<!-- <p class="custom-main-paragraph">código postal</p> -->
<q-input
<slot></slot>
<!-- <q-input
borderless
class="custom-main-paragraph"
v-model="postalCode"
@ -62,7 +64,7 @@ export default defineComponent({
mask="#####"
@blur="onBlur"
dense
/>
/> -->
</div>
</div>
</template>

View File

@ -5,6 +5,18 @@ import { defineComponent } from "vue";
export default defineComponent({
name: "range-component",
components: {},
props: {
min: {
type: Number,
default: 0,
required: true,
},
max: {
type: Number,
default: 200,
required: true,
},
},
setup() {
const rangePriceStore = useRangePriceStore();
@ -17,23 +29,17 @@ export default defineComponent({
<div class="range-container">
<p class="filter-item-paragraph">Precio</p>
<q-range
v-model="rangePriceStore.rangeValue"
@change="rangePriceStore.handlePriceRange"
:min="0"
:max="200"
color="primary"
/>
<slot></slot>
<div class="range-price-content">
<p class="filter-item-paragraph min-price">
Desde:
<span class="green-text">{{ rangePriceStore.rangeValue.min }}</span>
<span class="green-text">{{ min }}</span>
</p>
<p class="filter-item-paragraph max-price">
Hasta:
<span class="green-text">{{ rangePriceStore.rangeValue.max }}</span>
<span class="green-text">{{ max }}</span>
</p>
</div>
</div>

View File

@ -10,7 +10,7 @@ export default defineComponent({
const formStore = useFormStore();
const { sortProductFilters } = storeToRefs(formStore);
function handleOrder(order) {
async function handleOrder(order) {
sortProductFilters.value.order = order;
sortProductFilters.value.isOpenOrderFilter = false;
}

View File

@ -1,11 +1,12 @@
<script>
import { useIntersectionObserver } from "@vueuse/core";
import { storeToRefs } from "pinia";
import { defineComponent, ref } from "vue";
import { storeToRefs } from "pinia";
import Calendar from "src/components/@inputs/Calendar.vue";
import PostalCode from "src/components/@inputs/PostalCode.vue";
import IconSearch from "src/components/icons/IconSearch.vue";
import { usePostalCalendar } from "src/hooks/usePostalCalendar";
import { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
@ -17,21 +18,36 @@ export default defineComponent({
},
},
setup() {
const {
onSubmit,
fields: { calendar, calendarAttrs, postalCode, postalCodeAttrs },
errors,
} = usePostalCalendar({ type: "home" });
const mobileStore = useMobileStore();
const { screenWidth } = storeToRefs(mobileStore);
const { handleResize } = mobileStore;
const target = ref(null);
const navPos = ref("bottom");
useIntersectionObserver(target, ([{ isIntersecting }]) => {
mobileStore.isCarouselVisible = isIntersecting;
});
document.addEventListener("resize", handleResize);
return {
slide: ref("style"),
navPos,
target,
screenWidth,
errors,
onSubmit,
postalCode,
postalCodeAttrs,
calendar,
calendarAttrs,
};
},
components: { IconSearch, Calendar, PostalCode },
@ -75,19 +91,43 @@ export default defineComponent({
</p>
</header>
<div class="carousel-content-body">
<form @submit="onSubmit" class="carousel-content-body">
<div class="carousel-content-item">
<Calendar />
<!-- <Calendar /> -->
<Calendar>
<q-input
borderless
class="custom-date-input"
v-model="calendar"
v-bind="calendarAttrs"
:error="!!errors.date"
placeholder="Elige una fecha"
mask="##/##/####"
dense
/>
</Calendar>
</div>
<div class="carousel-content-item">
<PostalCode />
<!-- <PostalCode /> -->
<PostalCode>
<q-input
borderless
class="custom-main-paragraph"
v-model="postalCode"
v-bind="postalCodeAttrs"
:error="!!errors.postalCode"
placeholder="código postal"
mask="#####"
dense
/>
</PostalCode>
</div>
<q-btn to="/categoria/ramos" class="btn carousel-content-item">
<q-btn type="submit" class="btn carousel-content-item">
<IconSearch /> ver disponibilidad
</q-btn>
</div>
</form>
<!-- <footer class="carousel-content-footer"></footer> -->
</div>

View File

@ -1,5 +1,5 @@
<script>
import { defineComponent, ref } from "vue";
import { defineComponent } from "vue";
import IconQuestion from "../icons/IconQuestion.vue";
import LogoWhite from "../logo/LogoWhite.vue";
@ -10,11 +10,7 @@ export default defineComponent({
name: "question-component",
components: { Container, LogoWhite, IconQuestion, QuestionForm },
setup() {
const handleSubmit = () => {
console.log("Foi submit");
};
return { handleSubmit, text: ref(""), checkbox: ref(false) };
return {};
},
});
</script>

View File

@ -69,10 +69,8 @@ export default defineComponent({
class="swiper"
:space-between="screenWidth > 1024 ? 56 : 25"
:slides-per-view="'auto'"
:centered-slides="true"
:grabCursor="true"
:navigation="true"
:loop="true"
:pagination="{
dynamicBullets: true,
clickable: true,

View File

@ -6,12 +6,12 @@ export default defineComponent({
name: "card-component",
components: { IconEyes },
props: {
productValue: {
price: {
type: String,
default: "",
required: true,
},
productName: {
title: {
type: String,
default: "",
required: true,
@ -25,40 +25,42 @@ export default defineComponent({
default: "",
required: true,
},
imgClass: {
type: String,
default: "",
},
headClass: {
type: String,
default: "",
},
isNew: {
type: Boolean,
default: false,
},
size: {
type: String,
default: "",
default: "md-card",
},
alt: {
type: String,
default: "",
},
id: {
type: Number,
type: String,
required: true,
},
},
setup({ productValue, discount }) {
setup({ price, discount }) {
const isLoaded = ref(false);
const percent = +discount / 100;
const priceWithoutLetter = ~~price.replaceAll("€", "");
const finalValue = ~~(priceWithoutLetter - priceWithoutLetter * percent);
const onLoad = () => {
isLoaded.value = true;
};
const valueWithDiscount = () => {
const productWithouCaracters = ~~productValue.replaceAll("€", "");
if (discount) {
const finalValue = (+discount / 100) * productWithouCaracters;
return finalValue.toFixed(2);
}
};
return { onLoad, valueWithDiscount, isLoaded };
return { onLoad, isLoaded, finalValue, priceWithoutLetter };
},
});
</script>
@ -68,6 +70,7 @@ export default defineComponent({
<RouterLink
:to="`/product/${id}`"
class="card-container-head"
:class="[headClass]"
role="heading"
>
<div v-if="isNew || discount" class="tags">
@ -77,6 +80,7 @@ export default defineComponent({
<img
class="card-img"
:class="[imgClass]"
:src="imgSrc ? imgSrc : '../../assets/empty-img.jpg'"
:alt="alt"
:key="imgSrc"
@ -96,12 +100,12 @@ export default defineComponent({
</RouterLink>
<div class="card-container-body">
<p class="card-name" v-if="productName">{{ productName }}</p>
<p class="card-name" v-if="title">{{ title }}</p>
<div class="card-values">
<p class="price" v-if="productValue">{{ productValue }}</p>
<p class="price offer tachado" v-if="+valueWithDiscount > 0">
{{ valueWithDiscount() }}
<p class="price" v-if="finalValue">{{ finalValue }}</p>
<p class="price offer tachado" v-if="priceWithoutLetter !== finalValue">
{{ priceWithoutLetter }}
</p>
</div>
</div>
@ -114,30 +118,32 @@ export default defineComponent({
flex-direction: column;
gap: 12px;
user-select: none;
height: 100%;
&:focus {
outline: 2px solid $secondary;
}
&.sm-card {
width: min(100%, 166px);
& .card-container-head {
border-radius: 10px;
min-height: 188px;
}
}
&.md-card {
width: min(100%, 310px);
& .card-container-head {
min-height: 352px;
height: 300px;
@media only screen and (min-width: $med-hg) {
height: 352px;
}
@media only screen and (max-width: $med-md) {
border-radius: 10px;
height: 188px;
}
}
}
&.lg-card {
width: min(100%, 360px);
& .card-container-head {
min-height: 409px;
height: 409px;
@media only screen and (max-width: $med-lg) {
border-radius: 10px;
height: 188px;
}
}
}
@ -146,6 +152,14 @@ export default defineComponent({
overflow: hidden;
position: relative;
height: 100%;
max-height: 409px;
&.list-products-head {
min-height: 365px;
@media only screen and (max-width: $med-sm) {
min-height: 190px;
}
}
&:focus-visible {
outline: 2px solid $primary-light;
}
@ -202,6 +216,19 @@ export default defineComponent({
width: 100%;
height: 100%;
object-fit: cover;
&.list-products {
@media only screen and (min-width: calc($med-md + 1px)) {
min-height: 352px;
}
@media only screen and (max-width: $med-md) {
min-height: 190px;
}
}
&.carousel {
max-height: 410px;
}
}
& .skeleton {

View File

@ -1,7 +1,7 @@
<script>
import { defineComponent } from "vue";
import { useModalStore } from "src/stores/modalStore";
import { usePostalCalendar } from "src/hooks/usePostalCalendar";
import Calendar from "../@inputs/Calendar.vue";
import PostalCode from "../@inputs/PostalCode.vue";
import PriceRange from "../@inputs/PriceRange.vue";
@ -22,9 +22,29 @@ export default defineComponent({
type: String,
default: "",
},
typeModal: {
type: String,
default: "",
},
},
setup() {
const modalStore = useModalStore();
setup({ modalItem, typeModal }) {
const {
onSubmit,
fields: {
calendar,
calendarAttrs,
postalCode,
postalCodeAttrs,
priceRange,
priceRangeAttrs,
},
errors,
modalStore,
} = usePostalCalendar({
modalItem,
type: typeModal,
});
const modalTextContent = {
isOpenAvailability: {
title: "Disponibilidad",
@ -37,8 +57,17 @@ export default defineComponent({
};
return {
errors,
onSubmit,
modalStore,
modalTextContent,
postalCode,
postalCodeAttrs,
calendar,
calendarAttrs,
priceRange,
priceRangeAttrs,
};
},
});
@ -69,34 +98,82 @@ export default defineComponent({
</p>
<div class="modal-body-content">
<div v-if="modalItem === 'isOpenFilters'" class="modal-body-filters">
<div class="filter-field">
<PriceRange />
<form @submit="onSubmit" id="filters-form">
<div
v-if="modalItem === 'isOpenFilters'"
class="modal-body-filters"
>
<div class="filter-field">
<PriceRange :min="priceRange.min" :max="priceRange.max">
<q-range
v-model="priceRange"
v-bind="priceRangeAttrs"
:min="0"
:max="200"
color="primary"
/>
<!-- @change="rangePriceStore.handlePriceRange" -->
</PriceRange>
</div>
</div>
</div>
<div
v-if="modalItem === 'isOpenAvailability'"
class="modal-body-availability"
>
<calendar />
<div
v-if="modalItem === 'isOpenAvailability'"
class="modal-body-availability"
>
<Calendar>
<q-input
borderless
class="custom-date-input"
v-model="calendar"
v-bind="calendarAttrs"
:error="!!errors.date"
placeholder="Elige una fecha"
mask="##/##/####"
dense
/>
</Calendar>
<postal-code />
</div>
<PostalCode>
<q-input
borderless
class="custom-main-paragraph"
v-model="postalCode"
v-bind="postalCodeAttrs"
:error="!!errors.postalCode"
placeholder="código postal"
mask="#####"
dense
/>
</PostalCode>
</div>
</form>
</div>
</q-card-section>
<q-btn
flat
type="submit"
align="center"
class="modal-footer"
form="filters-form"
>
<!-- v-close-popup -->
<IconSearch />
<p>ver disponibilidad</p>
</q-btn>
<!-- <q-btn
flat
type="button"
align="center"
class="modal-footer"
@click="modalStore.handleSubmit({ isModalContent: modalItem })"
form="filters-form"
@click="handleSubmit({ content: modalItem })"
v-close-popup
>
<IconSearch />
<p>ver disponibilidad</p>
</q-btn>
</q-btn> -->
</q-card>
</q-dialog>
</template>

View File

@ -150,12 +150,9 @@ html {
grid-template-columns: repeat(auto-fill, minmax(min(100%, 310px), 1fr));
grid-auto-rows: 1fr;
gap: 47px;
&.row-md {
grid-auto-rows: 405px;
}
&.row-lg {
grid-auto-rows: 472px;
}
}
&.category-container {
width: min(100%, 1920px);
}
//! 1440px
@ -170,7 +167,7 @@ html {
padding-inline: 16px;
&.cards-container {
gap: 25px;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 166px), 1fr));
grid-template-columns: repeat(auto-fill, minmax(min(100%, 235px), 1fr));
}
}

View File

@ -0,0 +1,157 @@
import { toTypedSchema } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { useForm } from "vee-validate";
import { watch } from "vue";
import { useRouter } from "vue-router";
import { apiBack } from "src/boot/axios";
import { quasarNotify } from "src/functions/quasarNotify";
import { useCartStore } from "src/stores/cart";
import { useFormStore } from "src/stores/forms";
import { useModalStore } from "src/stores/modalStore";
import { useRangePriceStore } from "src/stores/rangePrice";
import { availabilitySchema } from "src/utils/zod/schemas";
import { rangePriceSchema } from "src/utils/zod/schemas/rangePriceSchema";
export function usePostalCalendar({ modalItem = "", type = "home" }) {
const { push } = useRouter();
const rangePriceStore = useRangePriceStore();
const { rangeValue } = storeToRefs(rangePriceStore);
const modalStore = useModalStore();
const { isOpenAvailability } = storeToRefs(modalStore);
const formStore = useFormStore();
const { availability, sortProductFilters } = storeToRefs(formStore);
const cartStore = useCartStore();
const { addToCart, getProducts } = cartStore;
const { currentProduct, products } = storeToRefs(cartStore);
const min = 0;
const max = 200;
const { handleSubmit, handleReset, defineField, errors } = useForm({
validationSchema: toTypedSchema(
type !== "filter" ? availabilitySchema : rangePriceSchema
),
initialValues: {
range: {
min,
max,
},
postalCode: "",
date: "",
},
});
const [calendar, calendarAttrs] = defineField("date");
const [postalCode, postalCodeAttrs] = defineField("postalCode");
const [priceRange, priceRangeAttrs] = defineField("range");
const [dedication, dedicationAttrs] = defineField("dedication");
watch(errors, (newErrors) => {
const errorsObj = {
postalCode: () =>
quasarNotify({ message: newErrors.postalCode, type: "erro" }),
date: () => quasarNotify({ message: newErrors.date, type: "erro" }),
range: () => quasarNotify({ message: newErrors.range, type: "erro" }),
};
const keys = Object.keys(newErrors);
keys.forEach((key) => {
errorsObj[key]();
});
});
const onSubmit = handleSubmit((values) => {
const postalAndDateParams = {
postalCode: values.postalCode,
dateExpired: values.date,
};
const objVal = {
home: async () => {
console.log(type);
getProducts(
{
itens: 30,
category: 1,
postalCode: values.postalCode,
dateExpired: values.date,
},
() => push("/categoria/ramos")
);
},
product: async () => {
console.log(type);
try {
const {
data: { data },
} = await apiBack.get(`products/slug/${currentProduct.value.id}`, {
params: postalAndDateParams,
});
} catch (error) {
console.error(error);
push("/");
}
},
availability: async () => {
console.log(type);
getProducts({
itens: 20,
postalCode: values.postalCode,
dateExpired: values.date,
});
},
filter: async () => {
console.log(type);
rangeValue.value.max = values.range.max;
rangeValue.value.min = values.range.min;
const category = sortProductFilters.value.category;
const categoryObj = {
plantas: 1,
ramos: 2,
};
const params = {
itens: 20,
category: categoryObj[category],
minPrice: values.range.min,
maxPrice: values.range.max,
};
getProducts(params);
},
};
objVal[type]() ||
console.error(
`INVALID TYPE! TYPE: ${type}, ONLY HOME, PRODUCT AND FILTER ARE VALID!`
);
if (modalItem) {
modalStore[modalItem] = false;
}
handleReset();
});
return {
onSubmit,
modalStore,
fields: {
calendar,
calendarAttrs,
postalCode,
postalCodeAttrs,
priceRange,
priceRangeAttrs,
dedication,
dedicationAttrs,
},
errors,
};
}

View File

@ -1,7 +1,10 @@
import { fakerES } from "@faker-js/faker";
export function generateFlowers({ length }) {
export function mockGenerator({ length }) {
const flowersMock = Array.from({ length }, (_, i) => {
const position = fakerES.datatype.boolean() ? i + 1 : undefined;
const position = fakerES.datatype.boolean() && i + 1;
const discount =
fakerES.datatype.boolean() &&
fakerES.commerce.price({ min: 5, max: 15, dec: 0 });
const flower = {
id: i + 1,
@ -19,31 +22,23 @@ export function generateFlowers({ length }) {
max: 60,
dec: 0,
}),
isNew: fakerES.datatype.boolean(),
slug: fakerES.commerce.isbn({ separator: "-", variant: 13 }),
category: fakerES.commerce.productMaterial(),
category: fakerES.number.int({ min: 1, max: 2 }),
postalCode: "12345",
dateExpired: "30/01/2024",
images: Array.from(
{ length: fakerES.number.int({ min: 2, max: 6 }) },
() => fakerES.image.urlPicsumPhotos()
),
featured: fakerES.datatype.boolean(),
featured: fakerES.number.int({ min: 0, max: 1 }),
};
if (position) {
flower.position = position;
}
if (position) flower.position = position;
if (discount) flower.discount = discount;
return flower;
});
return flowersMock;
}
// console.log(generateFlowers({ length: 100 }));
export const cardMock = Array.from({ length: 8 }, (_, i) => ({
id: i + 1,
imgSrc: `assets/flowers/flower-${i + 1}.png`,
title: fakerES.commerce.productName(),
discount: fakerES.commerce.price({ min: 5, max: 15, dec: 0 }),
isNew: fakerES.datatype.boolean(),
value: fakerES.commerce.price({ min: 20, max: 150 }),
}));

View File

@ -1,14 +1,6 @@
<script>
import { fakerES } from "@faker-js/faker";
import { storeToRefs } from "pinia";
import {
defineAsyncComponent,
defineComponent,
onUpdated,
reactive,
ref,
watch,
} from "vue";
import { defineComponent, onBeforeMount, onUpdated, ref, watch } from "vue";
import { useRoute } from "vue-router";
import SortSelect from "src/components/@inputs/SortSelect.vue";
@ -16,7 +8,11 @@ import IconArrowCircleFilledRight from "src/components/icons/IconArrowCircleFill
import IconArrowDownWhite from "src/components/icons/IconArrowDownWhite.vue";
import IconFilter from "src/components/icons/IconFilter.vue";
import IconPencil from "src/components/icons/IconPencil.vue";
import DudasSection from "src/components/sections/DudasSection.vue";
import Card from "src/components/ui/Card.vue";
import Container from "src/components/ui/Container.vue";
import Modal from "src/components/ui/Modal.vue";
import { useCartStore } from "src/stores/cart";
import { useFormStore } from "src/stores/forms";
import { useModalStore } from "src/stores/modalStore";
@ -28,18 +24,28 @@ export default defineComponent({
IconPencil,
IconFilter,
Container,
DudasSection: defineAsyncComponent(() =>
import("src/components/sections/DudasSection.vue")
),
Modal: defineAsyncComponent(() => import("src/components/ui/Modal.vue")),
Card: defineAsyncComponent(() => import("src/components/ui/Card.vue")),
DudasSection,
Modal,
Card,
SortSelect,
},
setup() {
const route = useRoute();
const modalStore = useModalStore();
const { openModal } = modalStore;
const formStore = useFormStore();
const { availability, sortProductFilters } = storeToRefs(formStore);
const monthES = reactive({
const cartStore = useCartStore();
const { products } = storeToRefs(cartStore);
const { getProducts } = cartStore;
const monthTest = ref("");
const isOpenOrder = ref(false);
const monthES = {
0: "Enero",
1: "Febrero",
2: "Marzo",
@ -52,37 +58,61 @@ export default defineComponent({
9: "Octubre",
10: "Noviembre",
11: "Diciembre",
});
const isOpenOrder = ref(false);
const route = useRoute();
const cardsMock = Array.from({ length: 8 }, (_, i) => ({
id: i + 1,
imgSrc: `../assets/flowers/flower-${i + 1}.png`,
discount: fakerES.number.int({ min: 5, max: 15 }),
isNew: fakerES.datatype.boolean(),
title: fakerES.commerce.product(),
value: fakerES.commerce.price({ min: 30, max: 100 }),
recommended: fakerES.datatype.boolean(),
date: fakerES.date.recent(),
}));
};
const orderText = {
"lowest-price": "menor precio",
"highest-price": "mayor precio",
recommended: "recomendados",
latest: "más recientes",
};
const categoryObj = {
plantas: 1,
ramos: 2,
};
watch(availability, (newDate) => {
const [_day, month, _year] = newDate.date.split("/");
monthTest.value = monthES[+month - 1];
console.log(monthTest.value);
});
watch(
() => route.path,
(newPatch) => {
sortProductFilters.value.category = newPatch.split("/")[2];
[() => route.path, () => sortProductFilters.value.order],
([newPath, newOrder]) => {
const categoryPath = newPath.split("/")[2];
sortProductFilters.value.category = categoryPath;
const params = {
category: categoryObj[categoryPath],
itens: window.screen.width <= 445 ? 16 : 20,
};
const paramsObj = {
"lowest-price": () => (params.lowPrice = 1),
"highest-price": () => (params.bigPrice = 1),
latest: () => (params.isNew = 1),
// recommended: () => params.featured = 1,
};
if (newOrder) {
paramsObj[newOrder]();
}
getProducts(params);
}
);
onBeforeMount(async () => {
const categoryPath = route.path.split("/")[2];
await getProducts({
category: categoryObj[categoryPath],
itens: window.screen.width <= 445 ? 16 : 20,
});
});
onUpdated(() => {
console.groupCollapsed("%c Updated!", "color: green;");
console.log(sortProductFilters.value);
console.log(availability.value);
console.groupEnd();
});
@ -94,11 +124,12 @@ export default defineComponent({
return {
sortProductFilters,
openOrderFilter,
openModal,
availability,
isOpenOrder,
modalStore,
orderText,
cardsMock,
products,
};
},
});
@ -138,7 +169,7 @@ export default defineComponent({
flat
class="btn filter-btn availability"
type="button"
@click="modalStore.openModal({ modal: 'availability' })"
@click="openModal({ modal: 'availability' })"
>
<IconPencil />
</q-btn>
@ -150,7 +181,7 @@ export default defineComponent({
flat
class="btn filter-item filters filter-btn"
type="button"
@click="modalStore.openModal({ modal: 'filters' })"
@click="openModal({ modal: 'filters' })"
>
<p class="filter-paragraph remove-mob">Filtros</p>
<IconFilter />
@ -186,19 +217,28 @@ export default defineComponent({
</header>
<div class="products-section-body">
<Container cardContainer>
<Card
v-for="(
{ imgSrc, discount, isNew, title, value, id }, i
) in cardsMock"
:productValue="value"
:productName="title"
:discount="discount.toString()"
:imgSrc="imgSrc"
:isNew="isNew"
:key="i"
:id="id"
/>
<Container cardContainer class="category-container">
<template
v-for="{
images,
discount,
isNew,
name,
price,
slug,
id,
} in products.data.products"
:key="id"
>
<Card
:price="price"
:title="name"
:discount="discount"
:imgSrc="images[0]"
:isNew="isNew"
:id="slug"
/>
</template>
</Container>
</div>
@ -210,8 +250,8 @@ export default defineComponent({
</section>
<dudas-section />
<modal modalItem="isOpenAvailability" />
<modal modalItem="isOpenFilters" />
<Modal modalItem="isOpenAvailability" typeModal="availability" />
<Modal modalItem="isOpenFilters" typeModal="filter" />
</q-page>
</template>

View File

@ -1,35 +1,36 @@
<script>
import { storeToRefs } from "pinia";
import { defineAsyncComponent, defineComponent, onBeforeMount, ref } from "vue";
import { defineComponent, onBeforeMount } from "vue";
import { apiBack } from "src/boot/axios";
import VerticalCarouselImgs from "src/components/quasar-components/carousel/VerticalCarouselImgs.vue";
import Swiper from "src/components/swiper/Swiper.vue";
import Card from "src/components/ui/Card.vue";
import Container from "src/components/ui/Container.vue";
import { cardMock } from "src/mock/cards";
import { useCartStore } from "src/stores/cart";
import { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
name: "HomePage",
components: {
VerticalCarouselImgs: defineAsyncComponent(() =>
import(
"src/components/quasar-components/carousel/VerticalCarouselImgs.vue"
)
),
Swiper: defineAsyncComponent(() =>
import("src/components/swiper/Swiper.vue")
),
Card: defineAsyncComponent({
loader: () => import("src/components/ui/Card.vue"),
loadingComponent: "<p>Loading</p>",
delay: 200,
timeout: 3000,
}),
VerticalCarouselImgs,
Container,
Swiper,
Card,
},
setup() {
const mobileStore = useMobileStore();
const { isCarouselVisible, isOpenNav, screenWidth } =
storeToRefs(mobileStore);
const cartStore = useCartStore();
const { getProducts } = cartStore;
const { products } = storeToRefs(cartStore);
onBeforeMount(async () => {
await getProducts();
});
/* const test = mockGenerator({ length: 100 });
console.log(test); */
const slidesContent = [
"assets/1.jpg",
@ -38,28 +39,13 @@ export default defineComponent({
"assets/4.jpg",
"assets/5.jpg",
];
const dataProduct = ref([]);
onBeforeMount(async () => {
try {
const { data } = await apiBack.get("/products");
dataProduct.value = data.data;
console.groupCollapsed("%c DATA FETCHED", "color: green;");
console.log(data.data);
console.groupEnd();
} catch (err) {
console.error(`FATAL ERROR ::: ${err}`);
}
});
return {
isCarouselVisible,
slidesContent,
screenWidth,
isOpenNav,
cardMock,
dataProduct,
products,
};
},
});
@ -85,17 +71,23 @@ export default defineComponent({
</header>
<div class="products-body">
<Container class="row-md" cardContainer>
<Container cardContainer>
<template
v-for="({ id, slug, name, price, images }, i) in dataProduct"
v-for="(
{ id, slug, name, price, images, isNew, discount }, i
) in products.data.products"
>
<Card
v-if="i < 8"
:id="slug"
:productName="name"
:productValue="price"
:price="price"
:title="name"
:discount="discount"
:imgSrc="images[0]"
:isNew="isNew"
:key="id"
imgClass="list-products"
size="md-card"
/>
</template>
</Container>
@ -122,21 +114,26 @@ export default defineComponent({
<div class="products-selection-body">
<q-no-ssr>
<Swiper>
<swiper-slide
v-for="{ id, discount, isNew, value, title, imgSrc } in cardMock"
:key="id"
class="swiper-slide"
<template
v-for="(
{ slug, discount, isNew, price, name, images }, i
) in products.data.products"
:key="slug"
>
<Card
:id="id"
:key="id"
:productValue="value"
:productName="title"
:discount="discount"
:imgSrc="imgSrc"
:isNew="isNew"
/>
</swiper-slide>
<swiper-slide class="swiper-slide" v-if="i < 10">
<Card
:id="slug"
:key="slug"
:price="price"
:title="name"
:discount="discount"
:imgSrc="images[0]"
:isNew="isNew"
imgClass="carousel"
size="lg-card"
/>
</swiper-slide>
</template>
</Swiper>
</q-no-ssr>
</div>

View File

@ -1,5 +1,4 @@
<script>
import { fakerES } from "@faker-js/faker";
import { storeToRefs } from "pinia";
import { useMeta } from "quasar";
import { useForm } from "vee-validate";
@ -18,9 +17,11 @@ import ProductCarousel from "components/quasar-components/carousel/ProductCarous
import DudasSection from "components/sections/DudasSection.vue";
import Card from "components/ui/Card.vue";
import Container from "components/ui/Container.vue";
import Modal from "components/ui/Modal.vue";
import { dedicationSchema } from "src/utils/zod/schemas";
import { useCartStore } from "stores/cart";
import { useModalStore } from "stores/modalStore";
export default defineComponent({
name: "ProductPage",
@ -37,23 +38,23 @@ export default defineComponent({
IconArrowCircleFilledRight,
IconArrowCircleFilledLeft,
ProductCarousel,
Modal,
},
setup() {
const route = useRoute();
const modalStore = useModalStore();
const { openModal } = modalStore;
const cartStore = useCartStore();
const { addToCart, getProduct } = cartStore;
const { getProduct, getProducts, products } = cartStore;
const { prevProduct, currentProduct, nextProduct, addCartLoadingBtn } =
storeToRefs(cartStore);
const cardMock = Array.from({ length: 4 }, (_, i) => ({
id: i + 1,
title: fakerES.lorem.word(),
attributes: [""],
imgSrc: `../assets/flowers/flower-${i + 1}.png`,
value: fakerES.commerce.price({ symbol: "€", min: 15, max: 200 }),
isNew: fakerES.datatype.boolean(),
discount: fakerES.number.int({ min: 5, max: 25 }),
}));
onBeforeMount(() => {
getProduct(route.params.id);
getProducts();
});
watch(currentProduct.value, (newValue) => {
useMeta(() => {
@ -85,14 +86,10 @@ export default defineComponent({
});
});
onBeforeMount(() => {
getProduct(+route.params.id);
});
watch(
() => route.params.id,
(newId) => {
getProduct(+newId);
getProduct(newId);
}
);
@ -107,27 +104,34 @@ export default defineComponent({
}
});
const category = reactive({
1: "Planta",
2: "Ramos",
});
const { handleSubmit, defineField, handleReset } = useForm({
validationSchema: dedicationSchema,
});
const [dedication, dedicationAttrs] = defineField("dedication");
const onSubmit = handleSubmit(() => {
addToCart(currentData.value, dedication);
handleReset();
});
/* const onSubmit = handleSubmit(() => {
openModal({ modal: "availability" });
// addToCart(currentData.value, dedication);
// handleReset();
}); */
return {
cardMock,
slide: ref(1),
fullscreen: ref(false),
dedication,
dedicationAttrs,
onSubmit,
products,
addCartLoadingBtn,
prevProduct,
currentProduct,
nextProduct,
currentData,
category,
openModal,
};
},
});
@ -137,31 +141,39 @@ export default defineComponent({
<q-page>
<Container class="product-container" tag="section">
<ProductCarousel>
<q-carousel-slide
v-for="(img, i) in currentProduct.value?.images"
:img-src="img"
class="product-gallery-item"
:name="i"
:key="i"
/>
<template v-for="(img, i) in currentProduct?.images" :key="i">
<q-carousel-slide
v-if="img"
:img-src="img"
class="product-gallery-item"
:name="i + 1"
/>
<q-carousel-slide
v-else
:img-src="'../assets/empty-img.jpg'"
class="product-gallery-item"
:name="1"
/>
</template>
</ProductCarousel>
<div class="product-content">
<header class="product-content-header">
<h3 class="product-content-title subtitle">
{{ currentProduct.value?.title }}
<q-skeleton type="rect" v-if="!currentProduct.value?.title" />
{{ currentProduct?.name }}
<q-skeleton type="rect" v-if="!currentProduct?.name" />
</h3>
<div class="product-header-block">
<p class="product-content-paragraph">
SKU:
<span class="green-text" style="display: inline-flex">
{{ currentProduct.value?.sku }}
{{ currentProduct?.slug }}
<q-skeleton
width="100px"
type="text"
v-if="!currentProduct.value?.sku"
v-if="!currentProduct?.slug"
/>
</span>
</p>
@ -169,11 +181,11 @@ export default defineComponent({
<p class="product-content-paragraph">
Categoría:
<span class="green-text">
{{ currentProduct.value?.category }}
{{ category[currentProduct?.category] }}
<q-skeleton
type="text"
width="50px"
v-if="!currentProduct.value?.category"
v-if="!currentProduct?.category"
/>
</span>
</p>
@ -183,29 +195,23 @@ export default defineComponent({
<div class="product-content-body">
<div class="product-content-paragraphs">
<p class="product-price green-text">
{{ currentProduct.value?.price }}
{{ currentProduct?.price }}
<q-skeleton
type="text"
height="90px"
width="80px"
v-if="!currentProduct.value?.price"
v-if="!currentProduct?.price"
/>
</p>
<p class="product-delivery green-text">Envío Gratuito</p>
<p class="product-description">
{{ currentProduct.value?.description }}
<q-skeleton
type="text"
v-if="!currentProduct.value?.description"
/>
<q-skeleton
type="text"
v-if="!currentProduct.value?.description"
/>
{{ currentProduct?.description }}
<q-skeleton type="text" v-if="!currentProduct?.description" />
<q-skeleton type="text" v-if="!currentProduct?.description" />
</p>
</div>
<form class="product-form" @submit="onSubmit">
<div class="product-form">
<div class="product-dedication">
<header class="product-dedication-header">
<IconPencilGreen />
@ -231,12 +237,12 @@ export default defineComponent({
<q-btn
:loading="addCartLoadingBtn"
type="submit"
color="primary"
class="btn sm-btn"
label="AÑADIR AL CARRITO"
@click="openModal({ modal: 'availability' })"
/>
</form>
</div>
</div>
<footer class="product-content-footer">
@ -299,6 +305,7 @@ export default defineComponent({
</Container>
<DudasSection isWhite />
<Modal modalItem="isOpenAvailability" typeModal="product" />
<Container class="like-another-container gray-bg" tag="section">
<header class="like-another-header">
@ -313,19 +320,20 @@ export default defineComponent({
</p>
</header>
<Container cardContainer>
<Container cardContainer class="no-padding">
<template
v-for="({ imgSrc, discount, isNew, title, value, id }, i) in cardMock"
v-for="({ images, discount, isNew, name, price, slug }, i) in products
.data.products"
>
<card
<Card
v-if="i < 4"
:productValue="value"
:productName="title"
:discount="discount.toString()"
:imgSrc="imgSrc"
:price="price"
:title="name"
:discount="discount"
:imgSrc="images[0]"
:isNew="isNew"
:key="i"
:id="id"
:key="slug"
:id="slug"
/>
</template>
</Container>

View File

@ -2,17 +2,29 @@ import { defineStore } from "pinia";
import { computed, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { api } from "src/boot/axios";
import { api, apiBack } from "src/boot/axios";
import { quasarNotify } from "src/functions/quasarNotify";
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 = 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) {
@ -60,16 +72,103 @@ export const useCartStore = defineStore("cart", () => {
}
getCart({ debug: true });
async function getProducts(
options = {
itens: undefined,
featured: undefined,
page: undefined,
category: undefined,
postalCode: undefined,
dateExpired: undefined,
minPrice: undefined,
maxPrice: undefined,
bigPrice: undefined,
lowPrice: undefined,
isNew: undefined,
},
navigate
) {
const optionsObj = {
itens: options.itens,
featured: options.featured,
page: options.page,
category: options.category,
postalCode: options.postalCode,
dateExpired: options.dateExpired,
minPrice: options.minPrice,
maxPrice: options.maxPrice,
bigPrice: options.bigPrice,
lowPrice: options.lowPrice,
isNew: options.isNew,
};
const validKeys = Object.keys(options).filter(
(key) => options[key] !== undefined
);
const params = validKeys.reduce((acc, key) => {
acc[key] = optionsObj[key];
return acc;
}, {});
try {
const {
data: { data },
} = await apiBack.get("products", {
params,
});
if (data[0].products.length === 0) {
return quasarNotify({
message:
"No hay productos disponibles para la fecha y el código postal seleccionados",
type: "erro",
});
}
products.value.data = data[0];
if (navigate) {
navigate();
}
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 DATA", "color: tomato;");
console.table(products.value.data);
console.groupEnd();
console.groupCollapsed("%c PREV PRODUCT", "color: tomato;");
console.table(products.value.prev);
console.groupEnd();
console.groupCollapsed("%c CURRENT PRODUCT", "color: tomato;");
console.table(products.value.current);
console.groupEnd();
console.groupCollapsed("%c NEXT PRODUCT", "color: tomato;");
console.table(products.value.next);
console.groupEnd();
console.groupEnd();
} catch (err) {
new Error(`FATAL ERROR ::: ${err}`);
}
}
/**
*
* @param id Id to get product
* @returns 'id: number; title: string; description: string; price: string; sku: string; category: string; images: string[]'
*
*/
async function getProduct(id) {
if (id) {
routeId.value = id;
try {
const promises = [
/* const promises = [
api.get(`flowers/${id - 1}`),
api.get(`flowers/${id}`),
api.get(`flowers/${id + 1}`),
@ -82,13 +181,18 @@ export const useCartStore = defineStore("cart", () => {
};
return result[res.status];
});
}); */
prevProduct.value = prev;
currentProduct.value = current;
nextProduct.value = next;
const { data } = await apiBack.get(`products/slug/${id}`);
console.groupCollapsed("%c Produtos recebido!", "color: green;");
prevProduct.value = {};
currentProduct.value = data.data[0];
nextProduct.value = {};
console.groupCollapsed(
`%c PRODUCT FETCHED! SLUG: ${routeId.value}`,
"color: green;"
);
console.time();
console.table(prevProduct.value);
console.table(currentProduct.value);
@ -139,7 +243,9 @@ export const useCartStore = defineStore("cart", () => {
currentProduct,
nextProduct,
addCartLoadingBtn,
products,
getProducts,
addToCart,
removeFromCart,
getProduct,

View File

@ -50,6 +50,10 @@ export const useFormStore = defineStore("forms", {
this.availability = values;
},
registerAvailability() {
console.log(this.availability);
},
handleCheckoutData(values) {
// console.log(values);
this.checkout = values;

View File

@ -1,7 +1,8 @@
import { defineStore } from "pinia";
import { ref } from "vue";
export const useLanguageStore = defineStore("language", {
state: () => ({
lang: "es",
}),
export const useLanguageStore = defineStore("language", () => {
const lang = ref("es");
return { lang };
});

View File

@ -1,21 +1,27 @@
import { defineStore } from "pinia";
import { ref } from "vue";
export const useMobileStore = defineStore("mobile", {
state: () => ({
isOpenNav: false,
isCarouselVisible: false,
screenWidth: 0,
}),
export const useMobileStore = defineStore("mobile", () => {
const isOpenNav = ref(false);
const isCarouselVisible = ref(false);
const screenWidth = ref(0);
actions: {
handleOpenMobileNav() {
this.isOpenNav = !this.isOpenNav;
},
handleResize() {
this.screenWidth = screen.width;
if (this.screenWidth > 768) {
this.isOpenNav = false;
}
},
},
function handleOpenMobileNav() {
isOpenNav.value = !isOpenNav.value;
}
function handleResize() {
screenWidth.value = screen.width;
if (screenWidth.value > 768) {
isOpenNav.value = false;
}
}
return {
isOpenNav,
isCarouselVisible,
screenWidth,
handleOpenMobileNav,
handleResize,
};
});

View File

@ -1,26 +1,31 @@
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { useFormStore } from "./forms";
export const useModalStore = defineStore("modal", {
state: () => ({
isOpenAvailability: false,
isOpenFilters: false,
}),
export const useModalStore = defineStore("modal", () => {
const formStore = useFormStore();
const availability = computed(() => formStore.availability);
actions: {
openModal({ modal }) {
const open = {
availability: () =>
(this.isOpenAvailability = !this.isOpenAvailability),
filters: () => (this.isOpenFilters = !this.isOpenFilters),
};
open[modal]();
},
handleSubmit({ isModalContent }) {
const isModal = {
isOpenAvailability: () => "Contenido modal availability",
isOpenFilters: () => "Contenido modal filters",
};
console.log(isModal[isModalContent]());
},
},
const isOpenAvailability = ref(false);
const isOpenFilters = ref(false);
function openModal({ modal }) {
const open = {
availability: () =>
(isOpenAvailability.value = !isOpenAvailability.value),
filters: () => (isOpenFilters.value = !isOpenFilters.value),
};
open[modal]();
}
function handleSubmit({ content }) {
const isModal = {
isOpenAvailability: () => "Contenido modal availability",
isOpenFilters: () => "Contenido modal filters",
};
console.log(availability.value);
console.log(isModal[content]());
}
return { openModal, handleSubmit, isOpenAvailability, isOpenFilters };
});

View File

@ -1,20 +1,17 @@
import { defineStore } from "pinia";
import { reactive } from "vue";
export const useRangePriceStore = defineStore("range-price", {
state: () => ({
rangeValue: {
min: 0,
max: 200,
},
}),
export const useRangePriceStore = defineStore("range-price", () => {
const rangeValue = reactive({
min: 0,
max: 200,
});
actions: {
handlePriceRange({ min, max }) {
console.log(min, max);
this.rangeValue = {
max,
min,
};
},
},
function handlePriceRange({ min, max }) {
console.log({ min, max });
rangeValue.min = min;
rangeValue.max = max;
}
return { rangeValue, handlePriceRange };
});

View File

@ -1,13 +1,14 @@
import { defineStore } from "pinia";
export const useTextInputStore = defineStore("text-input", {
state: () => ({
dedication: "",
}),
export const useTextInputStore = defineStore("text-input", () => {
const dedication = ref("");
actions: {
handleDedicationSubmit() {
console.log(this.dedication);
},
},
function handleDedicationSubmit() {
console.log(dedication);
}
return {
dedication,
handleDedicationSubmit,
};
});

View File

@ -1,8 +1,9 @@
import { z } from "zod";
import { postalCode } from "..";
import * as M from "../messages";
const availabilityObj = {
date: z.string().refine((val) => {
date: z.string({ required_error: M.requiredMessage }).refine((val) => {
const [day, month, year] = val.split("/");
const regex = /\//g;
const valWithoutSlash = val.replace(regex, "");

View File

@ -0,0 +1,12 @@
import { z } from "zod";
const rangePriceObj = {
range: z.object({
min: z
.number()
.refine((n) => n > 0, "El valor mínimo debe ser superior a cero"),
max: z.number(),
}),
};
export const rangePriceSchema = z.object(rangePriceObj);