Creamos api en node para la conexion y cambios que pidio pablo

This commit is contained in:
Jaume Solís 2024-01-11 11:16:45 +01:00
parent e92aa18cbd
commit 715e9f0ca0
67 changed files with 4973 additions and 1830 deletions

4
.env Normal file
View File

@ -0,0 +1,4 @@
STRIPE_PUBLISHABLE_KEY=''
STRIPE_ACCOUNT=''
API_VERSION=''
LOCALE=''

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ node_modules
# Quasar core related directories
.quasar
/dist
dist.rar
/quasar.config.*.temporary.compiled*
# Cordova related directories and files

View File

@ -0,0 +1,58 @@
const productsJson = require("./products.json")
class ProductController {
findAll(req, res) {
//Gerar produtos fake
/* const fakeProducts = generateFlowers(50)
console.log(fakeProducts); */
const params = req.query;
let productsFilter = productsJson
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)
})
}
}
else {
productsFilterPages.push({
page: 1,
itensPerProduct: totalItens,
products: productFilterItens
})
} */
return res.status(200).send({
data: productsFilter
})
}
findBySlug(req, res) {
const slug = req.params.slug
const products = productsJson
const filterSlug = products.filter(item => item.slug === slug)
return res.status(200).send({
data: filterSlug
})
}
}
module.exports = new ProductController();

1657
api/controller/products.json Normal file

File diff suppressed because it is too large Load Diff

14
api/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
</html>

27
api/index.js Normal file
View File

@ -0,0 +1,27 @@
const cors = require('cors');
const express = require('express');
const path = require('path');
const productController = require('./controller/product.controller');
const app = express();
const port = 9999;
const allowedOrigins = ['http://localhost:9100', 'https://floranet.onecommerce.dev/'];
const corsOptions = {
origin: allowedOrigins,
optionsSuccessStatus: 200,
};
app.use(cors(corsOptions));
app.get('/', (req, res) => {
const indexPath = path.join(__dirname, './', 'index.html');
res.sendFile(indexPath);
});
//Products
app.get('/api/products', productController.findAll);
app.get('/api/products/slug/:slug', productController.findBySlug);
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});

951
api/package-lock.json generated Normal file
View File

@ -0,0 +1,951 @@
{
"name": "api",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "api",
"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"
}
},
"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",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"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",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
"dependencies": {
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
"set-function-length": "^1.1.1"
},
"funding": {
"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",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.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",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"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==",
"dependencies": {
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"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==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"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",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"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=="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
"dependencies": {
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
"dependencies": {
"get-intrinsic": "^1.2.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"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==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ipaddr.js": {
"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==",
"engines": {
"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",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"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=="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"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",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"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=="
},
"node_modules/proxy-addr": {
"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==",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"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",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"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=="
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"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",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"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",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"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",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"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"
}
}
}
}

15
api/package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2"
}
}

619
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"@quasar/extras": "^1.16.4",
"@vee-validate/zod": "^4.12.2",
"@vue-stripe/vue-stripe": "^4.5.0",
"@vueuse/core": "^10.7.0",
"axios": "^1.2.1",
"pinia": "^2.0.11",
@ -31,7 +32,9 @@
"eslint-plugin-vue": "^9.0.0",
"json-server": "^0.17.4",
"postcss": "^8.4.14",
"prettier": "^2.5.1"
"prettier": "^2.5.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"engines": {
"node": "^18 || ^16 || ^14.19",
@ -59,6 +62,18 @@
"node": ">=6.0.0"
}
},
"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/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@ -107,9 +122,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.55.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz",
"integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==",
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -189,12 +204,12 @@
}
},
"node_modules/@intlify/core-base": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.8.0.tgz",
"integrity": "sha512-UxaSZVZ1DwqC/CltUZrWZNaWNhfmKtfyV4BJSt/Zt4Or/fZs1iFj0B+OekYk1+MRHfIOe3+x00uXGQI4PbO/9g==",
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.9.0.tgz",
"integrity": "sha512-C7UXPymDIOlMGSNjAhNLtKgzITc/8BjINK5gNKXg8GiWCTwL6n3MWr55czksxn8RM5wTMz0qcLOFT+adtaVQaA==",
"dependencies": {
"@intlify/message-compiler": "9.8.0",
"@intlify/shared": "9.8.0"
"@intlify/message-compiler": "9.9.0",
"@intlify/shared": "9.9.0"
},
"engines": {
"node": ">= 16"
@ -204,11 +219,11 @@
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.8.0.tgz",
"integrity": "sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ==",
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.9.0.tgz",
"integrity": "sha512-yDU/jdUm9KuhEzYfS+wuyja209yXgdl1XFhMlKtXEgSFTxz4COZQCRXXbbH8JrAjMsaJ7bdoPSLsKlY6mXG2iA==",
"dependencies": {
"@intlify/shared": "9.8.0",
"@intlify/shared": "9.9.0",
"source-map-js": "^1.0.2"
},
"engines": {
@ -219,9 +234,9 @@
}
},
"node_modules/@intlify/shared": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.8.0.tgz",
"integrity": "sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ==",
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.9.0.tgz",
"integrity": "sha512-1ECUyAHRrzOJbOizyGufYP2yukqGrWXtkmTu4PcswVnWbkcjzk3YQGmJ0bLkM7JZ0ZYAaohLGdYvBYnTOGYJ9g==",
"engines": {
"node": ">= 16"
},
@ -259,11 +274,30 @@
}
}
},
"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=="
},
"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/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -384,6 +418,22 @@
}
}
},
"node_modules/@quasar/app-vite/node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/@quasar/extras": {
"version": "1.16.9",
"resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.16.9.tgz",
@ -441,6 +491,35 @@
"node": ">= 8.0.0"
}
},
"node_modules/@stripe/stripe-js": {
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-1.54.2.tgz",
"integrity": "sha512-R1PwtDvUfs99cAjfuQ/WpwJ3c92+DAMy9xGApjqlWQMj0FKQabUAys2swfTRNzuYAYJh7NqK2dzcYVNkKLEKUg=="
},
"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/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@ -543,15 +622,18 @@
"dev": true
},
"node_modules/@types/node": {
"version": "12.20.55",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
"dev": true
"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/qs": {
"version": "6.9.10",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==",
"version": "6.9.11",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
"integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==",
"dev": true
},
"node_modules/@types/range-parser": {
@ -593,12 +675,12 @@
"dev": true
},
"node_modules/@vee-validate/zod": {
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@vee-validate/zod/-/zod-4.12.2.tgz",
"integrity": "sha512-+g9lk8hO0FJCDGfjhuxUtFV44NVowu7cBvJiNr2x8DpWhBUXcfcE+XAJB0JjDQeT1O9lgO+mskZ+k8EdbAk0xw==",
"version": "4.12.4",
"resolved": "https://registry.npmjs.org/@vee-validate/zod/-/zod-4.12.4.tgz",
"integrity": "sha512-iNFhkBfGkre2b+eBXgBpNlNVStxDrI59sJUbzBr01EjyTkFOUgc/0wPJrhY/kBp+0pnGzNi04jklJaKfNK2ibg==",
"dependencies": {
"type-fest": "^4.8.2",
"vee-validate": "4.12.2",
"type-fest": "^4.8.3",
"vee-validate": "4.12.4",
"zod": "^3.22.4"
}
},
@ -615,37 +697,46 @@
"vue": "^3.2.25"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.11.tgz",
"integrity": "sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==",
"node_modules/@vue-stripe/vue-stripe": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/@vue-stripe/vue-stripe/-/vue-stripe-4.5.0.tgz",
"integrity": "sha512-BU449XT5zegjNQirl+SSztbzGIvPjhxlHv8ybomSZcI1jB6qEpLgpk2eHMFDKnOGZZRhqtg4C5FiErwSJ/yuRw==",
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/shared": "3.3.11",
"@stripe/stripe-js": "^1.13.2",
"vue-coerce-props": "^1.0.0"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.7.tgz",
"integrity": "sha512-hhCaE3pTMrlIJK7M/o3Xf7HV8+JoNTGOQ/coWS+V+pH6QFFyqtoXqQzpqsNp7UK17xYKua/MBiKj4e1vgZOBYw==",
"dependencies": {
"@babel/parser": "^7.23.6",
"@vue/shared": "3.4.7",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz",
"integrity": "sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.7.tgz",
"integrity": "sha512-qDKBAIurCTub4n/6jDYkXwgsFuriqqmmLrIq1N2QDfYJA/mwiwvxi09OGn28g+uDdERX9NaKDLji0oTjE3sScg==",
"dependencies": {
"@vue/compiler-core": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-core": "3.4.7",
"@vue/shared": "3.4.7"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz",
"integrity": "sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.7.tgz",
"integrity": "sha512-Gec6CLkReVswDYjQFq79O5rktri4R7TsD/VPCiUoJw40JhNNxaNJJa8mrQrWoJluW4ETy6QN0NUyC/JO77OCOw==",
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-ssr": "3.3.11",
"@vue/reactivity-transform": "3.3.11",
"@vue/shared": "3.3.11",
"@babel/parser": "^7.23.6",
"@vue/compiler-core": "3.4.7",
"@vue/compiler-dom": "3.4.7",
"@vue/compiler-ssr": "3.4.7",
"@vue/shared": "3.4.7",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.32",
@ -653,12 +744,12 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz",
"integrity": "sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.7.tgz",
"integrity": "sha512-PvYeSOvnCkST5mGS0TLwEn5w+4GavtEn6adcq8AspbHaIr+mId5hp7cG3ASy3iy8b+LuXEG2/QaV/nj5BQ/Aww==",
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-dom": "3.4.7",
"@vue/shared": "3.4.7"
}
},
"node_modules/@vue/devtools-api": {
@ -667,69 +758,57 @@
"integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
},
"node_modules/@vue/reactivity": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.11.tgz",
"integrity": "sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.7.tgz",
"integrity": "sha512-F539DO0ogH0+L8F9Pnw7cjqibcmSOh5UTk16u5f4MKQ8fraqepI9zdh+sozPX6VmEHOcjo8qw3Or9ZcFFw4SZA==",
"dependencies": {
"@vue/shared": "3.3.11"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz",
"integrity": "sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==",
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
"@vue/shared": "3.3.11",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5"
"@vue/shared": "3.4.7"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.11.tgz",
"integrity": "sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.7.tgz",
"integrity": "sha512-QMMsWRQaD3BpGyjjChthpl4Mji4Fjx1qfdufsXlDkKU3HV+hWNor2z+29F+E1MmVcP0ZfRZUfqYgtsQoL7IGwQ==",
"dependencies": {
"@vue/reactivity": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/reactivity": "3.4.7",
"@vue/shared": "3.4.7"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz",
"integrity": "sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.7.tgz",
"integrity": "sha512-XwegyUY1rw8zxsX1Z36vwYcqo+uOgih5ti7y9vx+pPFhNdSQmN4LqK2RmSeAJG1oKV8NqSUmjpv92f/x6h0SeQ==",
"dependencies": {
"@vue/runtime-core": "3.3.11",
"@vue/shared": "3.3.11",
"csstype": "^3.1.2"
"@vue/runtime-core": "3.4.7",
"@vue/shared": "3.4.7",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.11.tgz",
"integrity": "sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.7.tgz",
"integrity": "sha512-3bWnYLEkLLhkDWqvNk7IvbQD4UcxvFKxELBiOO2iG3m6AniFIsBWfHOO5tLVQnjdWkODu4rq0GipmfEenVAK5Q==",
"dependencies": {
"@vue/compiler-ssr": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-ssr": "3.4.7",
"@vue/shared": "3.4.7"
},
"peerDependencies": {
"vue": "3.3.11"
"vue": "3.4.7"
}
},
"node_modules/@vue/shared": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.11.tgz",
"integrity": "sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw=="
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.7.tgz",
"integrity": "sha512-G+i4glX1dMJk88sbJEcQEGWRQnVm9eIY7CcQbO5dpdsD9SF8jka3Mr5OqZYGjczGN1+D6EUwdu6phcmcx9iuPA=="
},
"node_modules/@vueuse/core": {
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.0.tgz",
"integrity": "sha512-4EUDESCHtwu44ZWK3Gc/hZUVhVo/ysvdtwocB5vcauSV4B7NiGY5972WnsojB3vRNdxvAt7kzJWE2h9h7C9d5w==",
"version": "10.7.1",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.1.tgz",
"integrity": "sha512-74mWHlaesJSWGp1ihg76vAnfVq9NTv1YT0SYhAQ6zwFNdBkkP+CKKJmVOEHcdSnLXCXYiL5e7MaewblfiYLP7g==",
"dependencies": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.7.0",
"@vueuse/shared": "10.7.0",
"@vueuse/metadata": "10.7.1",
"@vueuse/shared": "10.7.1",
"vue-demi": ">=0.14.6"
},
"funding": {
@ -762,17 +841,17 @@
}
},
"node_modules/@vueuse/metadata": {
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.0.tgz",
"integrity": "sha512-GlaH7tKP2iBCZ3bHNZ6b0cl9g0CJK8lttkBNUX156gWvNYhTKEtbweWLm9rxCPIiwzYcr/5xML6T8ZUEt+DkvA==",
"version": "10.7.1",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.1.tgz",
"integrity": "sha512-jX8MbX5UX067DYVsbtrmKn6eG6KMcXxLRLlurGkZku5ZYT3vxgBjui2zajvUZ18QLIjrgBkFRsu7CqTAg18QFw==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "10.7.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.0.tgz",
"integrity": "sha512-kc00uV6CiaTdc3i1CDC4a3lBxzaBE9AgYNtFN87B5OOscqeWElj/uza8qVDmk7/U8JbqoONLbtqiLJ5LGRuqlw==",
"version": "10.7.1",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.1.tgz",
"integrity": "sha512-v0jbRR31LSgRY/C5i5X279A/WQjD6/JsMzGa+eqt658oJ75IvQXAeONmwvEMrvJQKnRElq/frzBR7fhmWY5uLw==",
"dependencies": {
"vue-demi": ">=0.14.6"
},
@ -819,9 +898,9 @@
}
},
"node_modules/acorn": {
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
"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"
@ -839,6 +918,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.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/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -982,6 +1070,12 @@
"safe-buffer": "~5.1.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/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -1052,11 +1146,11 @@
}
},
"node_modules/axios": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"dependencies": {
"follow-redirects": "^1.15.0",
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
@ -1303,9 +1397,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001568",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001568.tgz",
"integrity": "sha512-vSUkH84HontZJ88MiNrOau1EBrCqEQYgkC5gIySiDlpsm8sGVrhU7Kx4V6h0tnqaHzIHZv08HlJIwPbL4XL9+A==",
"version": "1.0.30001576",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz",
"integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==",
"dev": true,
"funding": [
{
@ -1695,6 +1789,12 @@
"node": ">= 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/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -1811,6 +1911,15 @@
"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/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -1845,9 +1954,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.4.610",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.610.tgz",
"integrity": "sha512-mqi2oL1mfeHYtOdCxbPQYV/PL7YrQlxbvFEZ0Ee8GbDdShimqt2/S6z2RWqysuvlwdOrQdqvE0KZrBTipAeJzg==",
"version": "1.4.625",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz",
"integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q==",
"dev": true
},
"node_modules/elementtree": {
@ -1886,6 +1995,17 @@
"once": "^1.4.0"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/errorhandler": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz",
@ -2282,15 +2402,15 @@
}
},
"node_modules/eslint": {
"version": "8.55.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz",
"integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==",
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.55.0",
"@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -2369,6 +2489,22 @@
"eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
@ -2405,31 +2541,6 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint/node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -2471,15 +2582,6 @@
"node": ">=0.10"
}
},
"node_modules/esquery/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@ -2492,7 +2594,7 @@
"node": ">=4.0"
}
},
"node_modules/esrecurse/node_modules/estraverse": {
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
@ -2647,9 +2749,9 @@
"dev": true
},
"node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
"integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dev": true,
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
@ -2675,9 +2777,9 @@
"dev": true
},
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz",
"integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
@ -2810,9 +2912,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
"integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"funding": [
{
"type": "individual",
@ -3790,6 +3892,12 @@
"node": ">=12"
}
},
"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",
@ -4382,9 +4490,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"funding": [
{
"type": "opencollective",
@ -4409,9 +4517,9 @@
}
},
"node_modules/postcss-selector-parser": {
"version": "6.0.13",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
"integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
"version": "6.0.15",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
"integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
"dev": true,
"dependencies": {
"cssesc": "^3.0.0",
@ -4500,9 +4608,9 @@
}
},
"node_modules/quasar": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.14.1.tgz",
"integrity": "sha512-TAIGUgHASlL7COS9qqfDKyV2+WGFcHQseIDTzN+yfXaXY5gn/FZqwkEnb87bgOqgGYw8KJerkfZg3aSel+bLPw==",
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.14.2.tgz",
"integrity": "sha512-f5KliWtM5BEuFsDU4yvuP+dlVIWZNrGu5VpWFsxzjpoykcP4B2HIOUiCl3mx2NCqERHd4Ts0aeioRkt9TTeExA==",
"engines": {
"node": ">= 10.18.1",
"npm": ">= 6.13.4",
@ -4744,9 +4852,9 @@
}
},
"node_modules/rollup-plugin-visualizer": {
"version": "5.11.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.11.0.tgz",
"integrity": "sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==",
"version": "5.12.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz",
"integrity": "sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==",
"dev": true,
"dependencies": {
"open": "^8.4.0",
@ -4832,9 +4940,9 @@
"dev": true
},
"node_modules/sass": {
"version": "1.69.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz",
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
"version": "1.69.7",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.7.tgz",
"integrity": "sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ==",
"dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
@ -4921,9 +5029,9 @@
"dev": true
},
"node_modules/serialize-javascript": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
"dependencies": {
"randombytes": "^2.1.0"
@ -5275,6 +5383,49 @@
"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/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@ -5294,9 +5445,9 @@
}
},
"node_modules/type-fest": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz",
"integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==",
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.9.0.tgz",
"integrity": "sha512-KS/6lh/ynPGiHD/LnAobrEFq3Ad4pBzOlJ1wAnJx9N4EYoqFhMfLIBjUT2UEx4wg5ZE+cC1ob6DCSpppVo+rtg==",
"engines": {
"node": ">=16"
},
@ -5318,17 +5469,16 @@
}
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"optional": true,
"peer": true,
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"devOptional": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
"node": ">=14.17"
}
},
"node_modules/uglify-js": {
@ -5343,6 +5493,12 @@
"node": ">=0.8.0"
}
},
"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/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@ -5421,6 +5577,12 @@
"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/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -5431,15 +5593,15 @@
}
},
"node_modules/vee-validate": {
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.12.2.tgz",
"integrity": "sha512-SF5AOHbyW8vy09FgMRVHxCtK/3D5Jsk0VyvMf/HZhwPgpjGCCBW6ZWDW11/HC6pRbMOjTcg3YueBI6hb3oveYg==",
"version": "4.12.4",
"resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.12.4.tgz",
"integrity": "sha512-rqSjMdl0l/RiGKywKhkXttUKwDlQOoxTxe31uMQiMlwK4Hbtlvr3OcQvpREp/qPTARxNKudKWCUVW/mfzuxUVQ==",
"dependencies": {
"@vue/devtools-api": "^6.5.1",
"type-fest": "^4.8.2"
"type-fest": "^4.8.3"
},
"peerDependencies": {
"vue": "^3.3.8"
"vue": "^3.3.11"
}
},
"node_modules/vite": {
@ -5480,15 +5642,15 @@
}
},
"node_modules/vue": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.11.tgz",
"integrity": "sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==",
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.7.tgz",
"integrity": "sha512-4urmkWpudekq0CPNMO7p6mBGa9qmTXwJMO2r6CT4EzIJVG7WoSReiysiNb7OSi/WI113oX0Srn9Rz1k/DCXKFQ==",
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-sfc": "3.3.11",
"@vue/runtime-dom": "3.3.11",
"@vue/server-renderer": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-dom": "3.4.7",
"@vue/compiler-sfc": "3.4.7",
"@vue/runtime-dom": "3.4.7",
"@vue/server-renderer": "3.4.7",
"@vue/shared": "3.4.7"
},
"peerDependencies": {
"typescript": "*"
@ -5499,10 +5661,15 @@
}
}
},
"node_modules/vue-coerce-props": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/vue-coerce-props/-/vue-coerce-props-1.0.0.tgz",
"integrity": "sha512-4fdRMXO6FHzmE7H4soAph6QmPg3sL/RiGdd+axuxuU07f02LNMns0jMM88fmt1bvSbN+2Wyd8raho6p6nXUzag=="
},
"node_modules/vue-eslint-parser": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
"integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
"version": "9.4.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.0.tgz",
"integrity": "sha512-7KsNBb6gHFA75BtneJsoK/dbZ281whUIwFYdQxA68QrCrGMXYzUMbPDHGcOQ0OocIVKrWSKWXZ4mL7tonCXoUw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
@ -5523,38 +5690,13 @@
"eslint": ">=6.0.0"
}
},
"node_modules/vue-eslint-parser/node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/vue-eslint-parser/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
},
"node_modules/vue-i18n": {
"version": "9.8.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.8.0.tgz",
"integrity": "sha512-Izho+6PYjejsTq2mzjcRdBZ5VLRQoSuuexvR8029h5CpN03FYqiqBrShMyf2I1DKkN6kw/xmujcbvC+4QybpsQ==",
"version": "9.9.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.9.0.tgz",
"integrity": "sha512-xQ5SxszUAqK5n84N+uUyHH/PiQl9xZ24FOxyAaNonmOQgXeN+rD9z/6DStOpOxNFQn4Cgcquot05gZc+CdOujA==",
"dependencies": {
"@intlify/core-base": "9.8.0",
"@intlify/shared": "9.8.0",
"@intlify/core-base": "9.9.0",
"@intlify/shared": "9.9.0",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
@ -5733,6 +5875,15 @@
"node": ">=12"
}
},
"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"
}
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@ -9,35 +9,41 @@
"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",
"build": "quasar build -m ssr",
"build:api": "cd api && tsc",
"start:build": "npm run build && cd dist/ssr && npm i && npm run start",
"backend": "json-server -p 5000 -d 600 -w src/services/json-server/db.json"
"backend": "json-server -p 3000 -d 600 -w src/services/json-server/db.json --routes src/services/json-server/routes.json"
},
"dependencies": {
"@vee-validate/zod": "^4.12.2",
"@vueuse/core": "^10.7.0",
"vee-validate": "^4.12.2",
"vue-image-zoomer": "^2.2.3",
"zod": "^3.22.4",
"axios": "^1.2.1",
"vue-i18n": "^9.0.0",
"pinia": "^2.0.11",
"@quasar/extras": "^1.16.4",
"@vee-validate/zod": "^4.12.2",
"@vue-stripe/vue-stripe": "^4.5.0",
"@vueuse/core": "^10.7.0",
"axios": "^1.2.1",
"express": "^4.18.2",
"pinia": "^2.0.11",
"quasar": "^2.6.0",
"vee-validate": "^4.12.2",
"vue": "^3.0.0",
"vue-router": "^4.0.0"
"vue-i18n": "^9.0.0",
"vue-image-zoomer": "^2.2.3",
"vue-router": "^4.0.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@faker-js/faker": "^8.3.1",
"json-server": "^0.17.4",
"eslint": "^8.10.0",
"eslint-plugin-vue": "^9.0.0",
"eslint-config-prettier": "^8.1.0",
"prettier": "^2.5.1",
"@intlify/vite-plugin-vue-i18n": "^3.3.1",
"@quasar/app-vite": "^1.3.0",
"autoprefixer": "^10.4.2",
"postcss": "^8.4.14"
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0",
"json-server": "^0.17.4",
"postcss": "^8.4.14",
"prettier": "^2.5.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"engines": {
"node": "^18 || ^16 || ^14.19",

BIN
public/assets/empty-img.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -119,7 +119,7 @@ module.exports = configure(function (/* ctx */) {
// directives: [],
// Quasar plugins
plugins: ["Meta", "Loading"],
plugins: ["Meta", "Loading", "Notify"],
},
// animations: 'all', // --- includes all animations

View File

@ -1,11 +1,11 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "App",
});
</script>
<template>
<router-view />
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App'
});
</script>

View File

@ -7,7 +7,8 @@ import { boot } from "quasar/wrappers";
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL: "http://localhost:5000/" });
const api = axios.create({ baseURL: "http://localhost:3000/jsonServer/" });
const apiBack = axios.create({ baseURL: "http://localhost:5000/api/" });
export default boot(({ app }) => {
// for use inside Vue files (Options API) through this.$axios and this.$api
@ -17,8 +18,9 @@ export default boot(({ app }) => {
// so you won't necessarily have to import axios in each vue file
app.config.globalProperties.$api = api;
app.config.globalProperties.$apiBack = apiBack;
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
// so you can easily perform requests against your app's API
});
export { api };
export { api, apiBack };

View File

@ -1,6 +1,6 @@
import { boot } from "quasar/wrappers";
import { createI18n } from "vue-i18n";
import messages from "src/i18n";
import { createI18n } from "vue-i18n";
export default boot(({ app }) => {
const i18n = createI18n({

View File

@ -1,3 +1,66 @@
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { quasarNotify } from "src/functions/quasarNotify";
import { useFormStore } from "src/stores/forms";
import { availabilitySchema } from "src/utils/zod/schemas/availabilitySchema";
import { useForm } from "vee-validate";
import { defineComponent, ref, watch } from "vue";
import IconCalendar from "../icons/IconCalendar.vue";
export default defineComponent({
name: "calendar-input",
components: { IconCalendar },
setup() {
const getDate = new Date();
const currentDay = getDate.getDate().toString().padStart(2, "0");
const currentMonth = getDate.getMonth() + 1;
const currentYear = getDate.getFullYear();
const fullCurrentDate = `${currentYear}/${currentMonth}/${currentDay}`;
const formStore = useFormStore();
const { availability } = storeToRefs(formStore);
const proxyDate = ref(fullCurrentDate);
const validationSchema = toTypedSchema(
availabilitySchema.pick({ date: true })
);
const { errors, defineField } = useForm({
validationSchema,
});
const [calendar, calendarAttrs] = defineField("date");
const onBlur = () => {
availability.value.date = calendar.value;
};
watch(errors, (newErrors) => {
if (newErrors.date) {
quasarNotify({ message: newErrors.date, type: "erro" });
}
});
return {
availability,
proxyDate,
calendar,
calendarAttrs,
errors,
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;
},
};
},
});
</script>
<template>
<div class="custom-input-el calendar">
<q-btn round size="sm" class="custom-date-btn">
@ -32,12 +95,10 @@
<p class="custom-head-paragraph">¿Cuándo?</p>
<q-input
class="custom-date-input"
label="Elige una fecha"
placeholder="DD/MM/YYYY"
placeholder="Elige una fecha"
v-model="calendar"
mask="##/##/####"
:error="!!errors.date"
:error-message="errors.date"
@blur="onBlur"
borderless
dense
@ -45,59 +106,3 @@
</div>
</div>
</template>
<script lang="js">
import { toTypedSchema } from '@vee-validate/zod';
import { storeToRefs } from 'pinia';
import { useFormStore } from 'src/stores/forms';
import { availabilitySchema } from 'src/utils/zod/schemas/availabilitySchema';
import { useForm } from 'vee-validate';
import { defineComponent, ref } from 'vue';
import IconCalendar from '../icons/IconCalendar.vue';
export default defineComponent({
name: 'calendar-input',
components: { IconCalendar },
setup() {
const getDate = new Date();
const currentDay = getDate.getDate().toString().padStart(2, '0');
const currentMonth = getDate.getMonth() + 1;
const currentYear = getDate.getFullYear();
const fullCurrentDate = `${currentYear}/${currentMonth}/${currentDay}`;
const formStore = useFormStore();
const { availability } = storeToRefs(formStore);
const proxyDate = ref(fullCurrentDate);
const validationSchema = toTypedSchema(
availabilitySchema.pick({ date: true })
);
const { errors, defineField } = useForm({
validationSchema,
});
const [calendar, calendarAttrs] = defineField('date');
const onBlur = () => {
availability.value.date = calendar.value;
};
return {
availability,
proxyDate,
calendar,
calendarAttrs,
errors,
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;
},
};
},
});
</script>

View File

@ -1,38 +1,16 @@
<template>
<div class="custom-input-el postal-code">
<IconPostalCode />
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { useForm } from "vee-validate";
import { defineComponent, watch } from "vue";
<div class="custom-block-content">
<p class="custom-head-paragraph">¿Dónde?</p>
<!-- <p class="custom-main-paragraph">código postal</p> -->
<q-input
borderless
class="custom-main-paragraph"
v-model="postalCode"
v-bind="postalCodeAttrs"
:error="!!errors.postalCode"
:error-message="errors.postalCode"
label="código postal"
placeholder="00000-000"
mask="#####-###"
@blur="onBlur"
dense
/>
</div>
</div>
</template>
<script lang="js">
import { toTypedSchema } from '@vee-validate/zod';
import { storeToRefs } from 'pinia';
import { useFormStore } from 'src/stores/forms';
import { availabilitySchema } from 'src/utils/zod/schemas/availabilitySchema';
import { useForm } from 'vee-validate';
import { defineComponent } from 'vue';
import IconPostalCode from '../icons/IconPostalCode.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: 'postal-code',
name: "postal-code",
components: { IconPostalCode },
setup() {
const formStore = useFormStore();
@ -46,11 +24,17 @@ export default defineComponent({
postalCode: availability.value.date,
},
});
const [postalCode, postalCodeAttrs] = defineField('postalCode');
const [postalCode, postalCodeAttrs] = defineField("postalCode");
const onBlur = () => {
availability.value.postalCode = postalCode.value ;
availability.value.postalCode = postalCode.value;
};
watch(errors, (newErrors) => {
if (newErrors.postalCode) {
quasarNotify({ message: newErrors.postalCode, type: "erro" });
}
});
return {
postalCode,
postalCodeAttrs,
@ -60,3 +44,25 @@ export default defineComponent({
},
});
</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> -->
<q-input
borderless
class="custom-main-paragraph"
v-model="postalCode"
v-bind="postalCodeAttrs"
:error="!!errors.postalCode"
placeholder="código postal"
mask="#####"
@blur="onBlur"
dense
/>
</div>
</div>
</template>

View File

@ -1,3 +1,18 @@
<script>
import { useRangePriceStore } from "src/stores/rangePrice";
import { defineComponent } from "vue";
export default defineComponent({
name: "range-component",
components: {},
setup() {
const rangePriceStore = useRangePriceStore();
return { rangePriceStore };
},
});
</script>
<template>
<div class="range-container">
<p class="filter-item-paragraph">Precio</p>
@ -24,21 +39,6 @@
</div>
</template>
<script lang="js">
import { useRangePriceStore } from 'src/stores/rangePrice';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'range-component',
components: {},
setup() {
const rangePriceStore = useRangePriceStore();
return { rangePriceStore };
},
});
</script>
<style lang="scss" scoped>
.range-container {
display: flex;

View File

@ -1,3 +1,25 @@
<script>
import { storeToRefs } from "pinia";
import { useFormStore } from "src/stores/forms";
import { defineComponent } from "vue";
export default defineComponent({
name: "SortSelect",
components: {},
setup() {
const formStore = useFormStore();
const { sortProductFilters } = storeToRefs(formStore);
function handleOrder(order) {
sortProductFilters.value.order = order;
sortProductFilters.value.isOpenOrderFilter = false;
}
return { sortProductFilters, handleOrder };
},
});
</script>
<template>
<div class="order-values" role="select">
<div
@ -34,28 +56,6 @@
</div>
</template>
<script lang="js">
import { storeToRefs } from 'pinia';
import { useFormStore } from 'src/stores/forms';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'SortSelect',
components: {},
setup() {
const formStore = useFormStore();
const { sortProductFilters } = storeToRefs(formStore);
function handleOrder(order) {
sortProductFilters.value.order = order;
sortProductFilters.value.isOpenOrderFilter = false;
}
return { sortProductFilters, handleOrder };
},
});
</script>
<style lang="scss" scoped>
.order-values {
display: flex;

View File

@ -1,3 +1,23 @@
<script>
import { defineComponent } from "vue";
import { ref } from "vue";
import IconArrowCircleRight from "../icons/IconArrowCircleRight.vue";
import LogoWhite from "../logo/LogoWhite.vue";
import Container from "../ui/Container.vue";
export default defineComponent({
name: "footer-component",
components: { Container, LogoWhite, IconArrowCircleRight },
setup() {
const currentYear = new Date().getFullYear();
const year = ref(currentYear);
return { year };
},
});
</script>
<template>
<q-footer class="custom-q-footer" elevated>
<container class="footer-container">
@ -26,10 +46,10 @@
<ul class="footer-list footer-secondary">
<li class="footer-list-item list-links">
<RouterLink to="/">Ramos</RouterLink><br />
<RouterLink to="/">Plantas</RouterLink><br />
<RouterLink to="/">Nosotros</RouterLink><br />
<RouterLink to="/faq">FAQs</RouterLink><br />
<RouterLink to="/categoria/ramos">Ramos</RouterLink><br />
<RouterLink to="/categoria/plantas">Plantas</RouterLink><br />
<!-- <RouterLink to="/">Nosotros</RouterLink><br />
<RouterLink to="/faq">FAQs</RouterLink><br /> -->
<RouterLink to="/contacta">Contacta</RouterLink>
</li>
@ -39,11 +59,11 @@
envío, calcular un nuevo envío o solicitar recogida, estamos para
ayudarte. <br /><br />
<RouterLink to="/">
<!-- <RouterLink to="/">
<IconArrowCircleRight /> Preguntas frecuentes
</RouterLink>
</RouterLink> -->
<br />
<RouterLink to="/">
<RouterLink to="/example">
<IconArrowCircleRight /> Contacta con nosotros
</RouterLink>
</p>
@ -51,11 +71,13 @@
<li class="footer-list-item">
<p class="footer-list-content">
Floranet &copy;2023 <br /><br />
<RouterLink to="/">Aviso Legal</RouterLink> <br />
<RouterLink to="/">Condiciones de uso</RouterLink><br />
<RouterLink to="/">Política de cookies</RouterLink><br />
<RouterLink to="/"> Política de Redes Sociales </RouterLink>
Floranet &copy;{{ year }} <br /><br />
<RouterLink to="/example">Aviso Legal</RouterLink> <br />
<RouterLink to="/example">Condiciones de uso</RouterLink><br />
<RouterLink to="/example">Política de cookies</RouterLink><br />
<RouterLink to="/example">
Política de Redes Sociales
</RouterLink>
<br /><br />
Desarrollado por diligent
@ -67,19 +89,6 @@
</q-footer>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import IconArrowCircleRight from '../icons/IconArrowCircleRight.vue';
import LogoWhite from '../logo/LogoWhite.vue';
import Container from '../ui/Container.vue';
export default defineComponent({
name: 'footer-component',
components: { Container, LogoWhite, IconArrowCircleRight },
});
</script>
<style lang="scss" scoped>
.custom-q-footer {
position: static;
@ -138,6 +147,16 @@ a:hover {
}
}
@media only screen and (max-width: $med-lg) {
&.footer-primary {
flex: 0 0 min(100%, 275px);
}
&.footer-secondary {
gap: 50px;
flex: 0 0 min(100%, 545px);
}
}
@media only screen and (max-width: $med-md) {
justify-content: space-evenly;
&.footer-primary {
@ -176,6 +195,9 @@ a:hover {
}
}
}
/* @media only screen and (max-width: $med-lg) and (max-width: 1200px) {
gap: initial;
} */
}
}
</style>

View File

@ -1,3 +1,28 @@
<script>
import { defineComponent } from "vue";
import { storeToRefs } from "pinia";
import { useMobileStore } from "src/stores/mobileNav";
import LogoGreenLight from "../logo/LogoGreenLight.vue";
import LogoWhite from "../logo/LogoWhite.vue";
import SendBanner from "../ui/SendBanner.vue";
import NavLinks from "./NavLinks.vue";
import UserArea from "./UserArea.vue";
export default defineComponent({
name: "header-primary",
components: { SendBanner, UserArea, NavLinks, LogoWhite, LogoGreenLight },
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
const { handleResize } = mobileStore;
window.addEventListener("resize", handleResize);
return { isOpenNav };
},
});
</script>
<template>
<q-header
class="header-container transparent"
@ -23,31 +48,6 @@
</q-header>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { useMobileStore } from 'src/stores/mobileNav';
import LogoGreenLight from '../logo/LogoGreenLight.vue';
import LogoWhite from '../logo/LogoWhite.vue';
import SendBanner from '../ui/SendBanner.vue';
import NavLinks from './NavLinks.vue';
import UserArea from './UserArea.vue';
export default defineComponent({
name: 'header-primary',
components: { SendBanner, UserArea, NavLinks, LogoWhite, LogoGreenLight },
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
const { handleResize } = mobileStore;
window.addEventListener('resize', handleResize);
return { isOpenNav };
},
});
</script>
<style lang="scss" scoped>
.header-container {
display: flex;

View File

@ -1,3 +1,26 @@
<script>
import { defineComponent } from "vue";
import { useMobileStore } from "src/stores/mobileNav";
import LogoWhite from "../logo/LogoWhite.vue";
import SendBanner from "../ui/SendBanner.vue";
import NavLinks from "./NavLinks.vue";
import UserArea from "./UserArea.vue";
export default defineComponent({
name: "header-secondary",
components: { SendBanner, UserArea, NavLinks, LogoWhite },
setup() {
const mobileStore = useMobileStore();
const { handleResize } = mobileStore;
window.addEventListener("resize", handleResize);
return;
},
});
</script>
<template>
<q-header class="header-container transparent">
<send-banner
@ -21,29 +44,6 @@
</q-header>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import { useMobileStore } from 'src/stores/mobileNav';
import LogoWhite from '../logo/LogoWhite.vue';
import SendBanner from '../ui/SendBanner.vue';
import NavLinks from './NavLinks.vue';
import UserArea from './UserArea.vue';
export default defineComponent({
name: 'header-secondary',
components: { SendBanner, UserArea, NavLinks, LogoWhite },
setup() {
const mobileStore = useMobileStore();
const { handleResize } = mobileStore;
window.addEventListener('resize', handleResize);
return;
},
});
</script>
<style lang="scss" scoped>
.header-container {
display: flex;

View File

@ -1,3 +1,15 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "NavLinks",
components: {},
setup() {
return {};
},
});
</script>
<template>
<nav class="menu-nav">
<ul class="links-list">
@ -24,18 +36,6 @@
</nav>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'NavLinks',
components: {},
setup() {
return {};
},
});
</script>
<style lang="scss" scoped>
.menu-nav .links-list {
display: flex;

View File

@ -1,13 +1,49 @@
<script>
import { defineComponent } from "vue";
import IconCart from "components/icons/IconCart.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 DropdownItem from "components/quasar-components/dropdown/DropdownItem.vue"; */
import { storeToRefs } from "pinia";
import { useCartStore } from "src/stores/cart";
import { useMobileStore } from "stores/mobileNav";
export default defineComponent({
name: "user-area",
components: {
IconCart,
// IconUser,
// DropdownGroup,
// DropdownItem,
IconHamburger,
},
setup() {
const mobileStore = useMobileStore();
const { handleOpenMobileNav } = mobileStore;
const cartStore = useCartStore();
const { cartLength } = storeToRefs(cartStore);
return { handleOpenMobileNav, cartLength };
},
});
</script>
<template>
<div class="user-area">
<dropdown-group class="user-area-lang" label="Idioma">
<!-- <dropdown-group class="user-area-lang" label="Idioma">
<dropdown-item current-lang="en"> EN </dropdown-item>
<dropdown-item current-lang="pt"> PT </dropdown-item>
<dropdown-item current-lang="es"> ES </dropdown-item>
</dropdown-group>
<RouterLink class="user-area-link user" to="/"><icon-user /></RouterLink> -->
<RouterLink class="user-area-link user" to="/"><icon-user /></RouterLink>
<RouterLink class="user-area-link cart" to="/checkout">
<RouterLink
class="user-area-link cart"
to="/checkout"
v-if="cartLength > 0"
>
<icon-cart />
<span class="cart-count" :class="cartLength > 0 && 'active'">
{{ cartLength }}
@ -27,37 +63,6 @@
</div>
</template>
<script>
import { defineComponent } from "vue";
import IconCart from "components/icons/IconCart.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 DropdownItem from "components/quasar-components/dropdown/DropdownItem.vue";
import { storeToRefs } from "pinia";
import { useCartStore } from "src/stores/cart";
import { useMobileStore } from "stores/mobileNav";
export default defineComponent({
name: "user-area",
components: {
IconCart,
IconUser,
DropdownGroup,
DropdownItem,
IconHamburger,
},
setup() {
const mobileStore = useMobileStore();
const { handleOpenMobileNav } = mobileStore;
const cartStore = useCartStore();
const { cartLength } = storeToRefs(cartStore);
return { handleOpenMobileNav, cartLength };
},
});
</script>
<style lang="scss" scoped>
.user-area {
display: flex;

View File

@ -1,3 +1,15 @@
<script>
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "product-carousel",
components: {},
setup() {
return { slide: ref(1), fullscreen: ref(false) };
},
});
</script>
<template>
<q-carousel
animated
@ -24,16 +36,4 @@
</q-carousel>
</template>
<script lang="js">
import { defineComponent, ref } from 'vue';
export default defineComponent({
name: 'product-carousel',
components: {},
setup() {
return { slide: ref(1), fullscreen: ref(false) };
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,43 @@
<script>
import { useIntersectionObserver } from "@vueuse/core";
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 { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
name: "vertical-carousel-imgs",
props: {
imgsArr: {
type: Array,
default: () => [""],
},
},
setup() {
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,
};
},
components: { IconSearch, Calendar, PostalCode },
});
</script>
<template>
<div
ref="target"
@ -8,7 +48,7 @@
navigation-position="right"
style="min-height: 100dvh"
v-model="slide"
:navigation="screenWidth >= 768"
navigation
autoplay
infinite
animated
@ -54,46 +94,6 @@
</div>
</template>
<script lang="js">
import { useIntersectionObserver } from '@vueuse/core';
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 { useMobileStore } from 'src/stores/mobileNav';
export default defineComponent({
name: 'vertical-carousel-imgs',
props: {
imgsArr: {
type: Array,
default: () => [''],
},
},
setup() {
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,
};
},
components: { IconSearch, Calendar, PostalCode },
});
</script>
<style lang="scss" scoped>
.vertical-carousel-container {
position: relative;
@ -106,7 +106,7 @@ export default defineComponent({
min-height: fit-content;
left: 0;
right: 0;
bottom: 70px;
bottom: 110px;
width: min(100%, 845px);
margin: 0 auto;
& .carousel-content-header {
@ -135,12 +135,11 @@ export default defineComponent({
flex-wrap: wrap;
border-radius: 10px 0 0 10px;
overflow: hidden;
/* margin-bottom: 82px; */
min-height: 150px;
min-height: 96px;
& .carousel-content-item {
display: flex;
align-items: center;
padding: 27px 53px 34px 48px;
padding-inline: 53px 34px;
flex: 1;
position: relative;
&:first-child::after {
@ -169,7 +168,7 @@ export default defineComponent({
&:last-child {
border-radius: 0 10px 10px 0;
padding: 27px 56px 34px;
padding-inline: 56px;
}
}
}

View File

@ -1,3 +1,18 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "dropdown-group",
props: {
label: {
type: String,
default: "",
required: true,
},
},
});
</script>
<template>
<q-btn-dropdown :label="label">
<q-list>
@ -6,21 +21,6 @@
</q-btn-dropdown>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'dropdown-group',
props: {
label: {
type: String,
default: '',
required: true,
},
},
});
</script>
<style lang="scss" scoped>
.q-btn {
text-transform: initial;

View File

@ -1,24 +1,14 @@
<template>
<q-item clickable v-close-popup @click="onItemClick(currentLang)">
<q-item-section>
<q-item-label>
<slot></slot>
</q-item-label>
</q-item-section>
</q-item>
</template>
<script lang="js">
import { storeToRefs } from 'pinia';
import { useLanguageStore } from 'src/stores/language';
import { defineComponent } from 'vue';
<script>
import { storeToRefs } from "pinia";
import { useLanguageStore } from "src/stores/language";
import { defineComponent } from "vue";
export default defineComponent({
name: 'DropdownItem',
name: "DropdownItem",
props: {
currentLang: {
type: String,
default: '',
default: "",
},
},
setup() {
@ -33,3 +23,13 @@ export default defineComponent({
},
});
</script>
<template>
<q-item clickable v-close-popup @click="onItemClick(currentLang)">
<q-item-section>
<q-item-label>
<slot></slot>
</q-item-label>
</q-item-section>
</q-item>
</template>

View File

@ -1,3 +1,20 @@
<script>
import { defineComponent } from "vue";
import IconChatRoundedFill from "../icons/IconChatRoundedFill.vue";
import Container from "../ui/Container.vue";
export default defineComponent({
name: "dudas-section",
components: { IconChatRoundedFill, Container },
props: {
isWhite: {
type: Boolean,
defaul: false,
},
},
});
</script>
<template>
<section
class="questions-section"
@ -21,23 +38,6 @@
</section>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import IconChatRoundedFill from '../icons/IconChatRoundedFill.vue';
import Container from '../ui/Container.vue';
export default defineComponent({
name: 'dudas-section',
components: { IconChatRoundedFill, Container },
props: {
isWhite: {
type: Boolean,
defaul: false,
},
},
});
</script>
<style lang="scss" scoped>
.questions-section {
background-color: $secondary-10;

View File

@ -1,3 +1,21 @@
<script>
import { defineComponent } from "vue";
import IconCarr from "../icons/IconCar.vue";
import IconChatRoundedFill from "../icons/IconChatRoundedFill.vue";
import IconLocation from "../icons/IconLocation.vue";
// import Container from '../ui/Container.vue';
export default defineComponent({
name: "info-section",
components: {
IconChatRoundedFill,
IconLocation,
IconCarr,
},
});
</script>
<template>
<section class="info-container">
<div class="info-content amplia">
@ -40,24 +58,6 @@
</section>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import IconCarr from '../icons/IconCar.vue';
import IconChatRoundedFill from '../icons/IconChatRoundedFill.vue';
import IconLocation from '../icons/IconLocation.vue';
// import Container from '../ui/Container.vue';
export default defineComponent({
name: 'info-section',
components: {
IconChatRoundedFill,
IconLocation,
IconCarr,
},
});
</script>
<style lang="scss" scoped>
.info-container {
display: flex;

View File

@ -1,3 +1,24 @@
<script>
import { defineComponent, ref } from "vue";
import IconQuestion from "../icons/IconQuestion.vue";
import LogoWhite from "../logo/LogoWhite.vue";
import Container from "../ui/Container.vue";
import QuestionForm from "../ui/QuestionForm.vue";
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) };
},
});
</script>
<template>
<section
class="question-container"
@ -32,27 +53,6 @@
</section>
</template>
<script lang="js">
import { defineComponent, ref } from 'vue';
import IconQuestion from '../icons/IconQuestion.vue';
import LogoWhite from '../logo/LogoWhite.vue';
import Container from '../ui/Container.vue';
import QuestionForm from '../ui/QuestionForm.vue';
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) };
},
});
</script>
<style lang="scss" scoped>
p {
color: $white;

View File

@ -1,3 +1,29 @@
<script>
import { defineComponent } from "vue";
import IconChatRoundedFill from "../icons/IconChatRoundedFill.vue";
import IconCheck from "../icons/IconCheck.vue";
import IconFlower from "../icons/IconFlower.vue";
import IconQuality from "../icons/IconQuality.vue";
import IconService from "../icons/IconService.vue";
import IconShop from "../icons/IconShop.vue";
import LogoWhite from "../logo/LogoWhite.vue";
import Container from "../ui/Container.vue";
export default defineComponent({
name: "reasons-section",
components: {
Container,
LogoWhite,
IconChatRoundedFill,
IconQuality,
IconFlower,
IconShop,
IconService,
IconCheck,
},
});
</script>
<template>
<section
class="reasons-container"
@ -84,32 +110,6 @@
</section>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import IconChatRoundedFill from '../icons/IconChatRoundedFill.vue';
import IconCheck from '../icons/IconCheck.vue';
import IconFlower from '../icons/IconFlower.vue';
import IconQuality from '../icons/IconQuality.vue';
import IconService from '../icons/IconService.vue';
import IconShop from '../icons/IconShop.vue';
import LogoWhite from '../logo/LogoWhite.vue';
import Container from '../ui/Container.vue';
export default defineComponent({
name: 'reasons-section',
components: {
Container,
LogoWhite,
IconChatRoundedFill,
IconQuality,
IconFlower,
IconShop,
IconService,
IconCheck,
},
});
</script>
<style lang="scss" scoped>
p,
h5 {

View File

@ -1,3 +1,57 @@
<script>
import { storeToRefs } from "pinia";
import { defineComponent, onMounted, ref } from "vue";
import { useMobileStore } from "src/stores/mobileNav";
import IconArrowCircleFilledLeft from "../icons/IconArrowCircleFilledLeft.vue";
import IconArrowCircleFilledRight from "../icons/IconArrowCircleFilledRight.vue";
export default defineComponent({
name: "SwiperComponent",
components: { IconArrowCircleFilledLeft, IconArrowCircleFilledRight },
setup() {
const mobileStore = useMobileStore();
const { screenWidth } = storeToRefs(mobileStore);
const prevBtn = ref(null);
const nextBtn = ref(null);
const swiperContainer = ref(null);
const prevSwiperBtn = ref(null);
const nextSwiperBtn = ref(null);
onMounted(() => {
// console.log('Montado!');
swiperContainer.value =
document.querySelector("swiper-container").shadowRoot;
prevSwiperBtn.value = swiperContainer.value.querySelector(
".swiper-button-prev"
);
nextSwiperBtn.value = swiperContainer.value.querySelector(
".swiper-button-next"
);
const swiperDisplay = "none";
nextSwiperBtn.value.style.display = swiperDisplay;
prevSwiperBtn.value.style.display = swiperDisplay;
nextBtn.value = document.querySelector(".swiper-btn.next");
prevBtn.value = document.querySelector(".swiper-btn.prev");
});
return {
screenWidth,
handlePrev() {
// console.log('Prev click');
prevSwiperBtn.value.click();
},
handleNext() {
// console.log('Next click');
nextSwiperBtn.value.click();
},
};
},
});
</script>
<template>
<q-btn
title="previous button"
@ -43,72 +97,6 @@
</q-btn>
</template>
<script lang="js">
import { storeToRefs } from 'pinia';
import { defineComponent, onMounted, ref } from 'vue';
import { useMobileStore } from 'src/stores/mobileNav';
import IconArrowCircleFilledLeft from '../icons/IconArrowCircleFilledLeft.vue';
import IconArrowCircleFilledRight from '../icons/IconArrowCircleFilledRight.vue';
export default defineComponent({
name: 'SwiperComponent',
components: { IconArrowCircleFilledLeft, IconArrowCircleFilledRight },
setup() {
const mobileStore = useMobileStore();
const { screenWidth } = storeToRefs(mobileStore);
const prevBtn = ref(null);
const nextBtn = ref(null);
const swiperContainer = ref(null);
const prevSwiperBtn = ref(null);
const nextSwiperBtn = ref(null);
onMounted(() => {
// console.log('Montado!');
swiperContainer.value =
document.querySelector('swiper-container').shadowRoot;
prevSwiperBtn.value = swiperContainer.value.querySelector(
'.swiper-button-prev'
);
nextSwiperBtn.value = swiperContainer.value.querySelector(
'.swiper-button-next'
);
const swiperDisplay = 'none';
nextSwiperBtn.value.style.display = swiperDisplay;
prevSwiperBtn.value.style.display = swiperDisplay;
nextBtn.value = document.querySelector('.swiper-btn.next');
prevBtn.value = document.querySelector('.swiper-btn.prev');
});
/* onUpdated(() => {
console.log('Atualizado!');
console.groupCollapsed('%c Custom', 'color: tomato;');
console.log({ prevBtn: prevBtn.value, nextBtn: nextBtn.value });
console.groupEnd();
console.groupCollapsed('%c Swiper', 'color: hotpink;');
console.log(prevSwiperBtn.value);
console.groupEnd();
}); */
return {
screenWidth,
handlePrev() {
// console.log('Prev click');
prevSwiperBtn.value.click();
},
handleNext() {
// console.log('Next click');
nextSwiperBtn.value.click();
},
};
},
});
</script>
<style lang="scss">
.swiper {
height: 100%;

View File

@ -1,15 +1,15 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "button-component",
});
</script>
<template>
<button class="btn" type="button">
<slot></slot>
</button>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'button-component',
});
</script>
<style lang="scss"></style>

View File

@ -1,3 +1,68 @@
<script>
import { defineComponent, ref } from "vue";
import IconEyes from "../icons/IconEyes.vue";
export default defineComponent({
name: "card-component",
components: { IconEyes },
props: {
productValue: {
type: String,
default: "",
required: true,
},
productName: {
type: String,
default: "",
required: true,
},
discount: {
type: String,
default: "",
},
imgSrc: {
type: String,
default: "",
required: true,
},
isNew: {
type: Boolean,
default: false,
},
size: {
type: String,
default: "",
},
alt: {
type: String,
default: "",
},
id: {
type: Number,
required: true,
},
},
setup({ productValue, discount }) {
const isLoaded = ref(false);
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 };
},
});
</script>
<template>
<div class="card-container" :class="size">
<RouterLink
@ -12,9 +77,16 @@
<img
class="card-img"
:src="imgSrc ? imgSrc : '../../assets/empty-img.png'"
:src="imgSrc ? imgSrc : '../../assets/empty-img.jpg'"
:alt="alt"
@load="handleLoad"
:key="imgSrc"
@load="onLoad"
/>
<q-skeleton
v-if="!isLoaded"
class="skeleton"
width="100%"
height="100%"
/>
<div class="head-hovered">
@ -27,7 +99,7 @@
<p class="card-name" v-if="productName">{{ productName }}</p>
<div class="card-values">
<p class="price" v-if="productValue">{{ productValue }}</p>
<p class="price" v-if="productValue">{{ productValue }}</p>
<p class="price offer tachado" v-if="+valueWithDiscount > 0">
{{ valueWithDiscount() }}
</p>
@ -36,71 +108,6 @@
</div>
</template>
<script lang="js">
import { defineComponent, ref } from 'vue';
import IconEyes from '../icons/IconEyes.vue';
export default defineComponent({
name: 'card-component',
components: { IconEyes },
props: {
productValue: {
type: String,
default: '',
required: true,
},
productName: {
type: String,
default: '',
required: true,
},
discount: {
type: String,
default: '',
},
imgSrc: {
type: String,
default: '',
required: true,
},
isNew: {
type: Boolean,
default: false,
},
size: {
type: String,
default: '',
},
alt: {
type: String,
default: '',
},
id: {
type: Number,
required: true,
},
},
setup({ productValue, discount }) {
const loadingImg = ref(true);
const handleLoad = () => {
loadingImg.value = false;
};
const valueWithDiscount = () => {
const productWithouCaracters = ~~productValue.replaceAll('€', '');
if (discount) {
const finalValue = (+discount / 100) * productWithouCaracters;
return finalValue.toFixed(2);
}
};
return { handleLoad, valueWithDiscount };
},
});
</script>
<style lang="scss" scoped>
.card-container {
display: flex;
@ -138,6 +145,7 @@ export default defineComponent({
border-radius: 15px;
overflow: hidden;
position: relative;
height: 100%;
&:focus-visible {
outline: 2px solid $primary-light;
}
@ -193,10 +201,16 @@ export default defineComponent({
& .card-img {
width: 100%;
height: 100%;
object-fit: cover;
}
& .skeleton {
position: absolute;
inset: 0;
background-color: $primary;
opacity: 0.5;
}
& .head-hovered {
position: absolute;
inset: 0;

View File

@ -1,21 +1,21 @@
<template>
<q-btn flat @click="handleClick"><IconChat /></q-btn>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import IconChat from '../icons/IconChat.vue';
<script>
import { defineComponent } from "vue";
import IconChat from "../icons/IconChat.vue";
export default defineComponent({
name: 'chat-component',
name: "chat-component",
components: { IconChat },
setup() {
const handleClick = () => {
console.log('click');
console.log("click");
};
return { handleClick };
},
});
</script>
<template>
<q-btn flat @click="handleClick"><IconChat /></q-btn>
</template>
<style lang="scss" scoped></style>

View File

@ -1,13 +1,3 @@
<template>
<component
:is="tag"
class="custom-container"
:class="cardContainer && 'cards-container'"
>
<slot></slot>
</component>
</template>
<script>
import { defineComponent } from "vue";
@ -29,3 +19,13 @@ export default defineComponent({
},
});
</script>
<template>
<component
:is="tag"
class="custom-container"
:class="cardContainer && 'cards-container'"
>
<slot></slot>
</component>
</template>

View File

@ -1,45 +1,12 @@
<template>
<div class="mobile-nav-container" :class="!isOpenNav && 'hide'">
<header class="mobile-nav-links">
<RouterLink @click="closeNav" class="mobile-link" to="/categoria/ramos"
>Ramos</RouterLink
>
<RouterLink @click="closeNav" class="mobile-link" to="/categoria/plantas">
Plantas
</RouterLink>
<RouterLink @click="closeNav" class="mobile-link" to="/"
>Floranet</RouterLink
>
<RouterLink @click="closeNav" class="mobile-link" to="/">FAQs</RouterLink>
<RouterLink @click="closeNav" class="mobile-link" to="/"
>Contacta</RouterLink
>
</header>
<script>
import { useMobileStore } from "src/stores/mobileNav";
<div class="mobile-nav-lang">
<p class="mobile-lang-paragraph">Idioma</p>
<select class="mobile-lang-select" name="lang">
<option value="">Español</option>
</select>
</div>
<footer class="mobile-nav-footer">
<RouterLink to="/" class="btn outlined rounded sm-btn">
¿Tienes dudas? hablemos <IconChatRoundedFill />
</RouterLink>
</footer>
</div>
</template>
<script lang="js">
import { useMobileStore } from 'src/stores/mobileNav';
import { storeToRefs } from 'pinia';
import { defineComponent, watch } from 'vue';
import IconChatRoundedFill from '../icons/IconChatRoundedFill.vue';
import { storeToRefs } from "pinia";
import { defineComponent, watch } from "vue";
import IconChatRoundedFill from "../icons/IconChatRoundedFill.vue";
export default defineComponent({
name: 'mobile-nav',
name: "mobile-nav",
components: { IconChatRoundedFill },
setup() {
const mobileStore = useMobileStore();
@ -51,16 +18,16 @@ export default defineComponent({
function closeNav() {
isOpenNav.value = false;
console.log('foi click');
console.log("foi click");
}
watch(isOpenNav, (newValue) => {
if (newValue) {
setBodyStyle('hidden');
setBodyStyle("hidden");
window.scrollTo(0, 0);
return;
}
setBodyStyle('visible');
setBodyStyle("visible");
});
return { isOpenNav, closeNav };
@ -68,6 +35,39 @@ export default defineComponent({
});
</script>
<template>
<div class="mobile-nav-container" :class="!isOpenNav && 'hide'">
<header class="mobile-nav-links">
<RouterLink @click="closeNav" class="mobile-link" to="/categoria/ramos">
Ramos
</RouterLink>
<RouterLink @click="closeNav" class="mobile-link" to="/categoria/plantas">
Plantas
</RouterLink>
<RouterLink @click="closeNav" class="mobile-link" to="/example">
Floranet
</RouterLink>
<!-- <RouterLink @click="closeNav" class="mobile-link" to="/">FAQs</RouterLink> -->
<RouterLink @click="closeNav" class="mobile-link" to="/example">
Contacta
</RouterLink>
</header>
<!-- <div class="mobile-nav-lang">
<p class="mobile-lang-paragraph">Idioma</p>
<select class="mobile-lang-select" name="lang">
<option value="">Español</option>
</select>
</div> -->
<footer class="mobile-nav-footer">
<RouterLink to="/" class="btn outlined rounded sm-btn">
¿Tienes dudas? hablemos <IconChatRoundedFill />
</RouterLink>
</footer>
</div>
</template>
<style lang="scss" scoped>
.mobile-nav-container {
position: absolute;

View File

@ -1,3 +1,49 @@
<script>
import { defineComponent } from "vue";
import { useModalStore } from "src/stores/modalStore";
import Calendar from "../@inputs/Calendar.vue";
import PostalCode from "../@inputs/PostalCode.vue";
import PriceRange from "../@inputs/PriceRange.vue";
import IconCloseModal from "../icons/IconCloseModal.vue";
import IconSearch from "../icons/IconSearch.vue";
export default defineComponent({
name: "modal-component",
components: {
IconSearch,
IconCloseModal,
PriceRange,
Calendar,
PostalCode,
},
props: {
modalItem: {
type: String,
default: "",
},
},
setup() {
const modalStore = useModalStore();
const modalTextContent = {
isOpenAvailability: {
title: "Disponibilidad",
subtitle: "Elige a dónde y cuando quieres enviar el ramo",
},
isOpenFilters: {
title: "Filtros",
subtitle: "Personaliza tu búsqueda",
},
};
return {
modalStore,
modalTextContent,
};
},
});
</script>
<template>
<q-dialog v-model="modalStore[modalItem]" class="modal-container">
<q-card class="modal-content">
@ -55,53 +101,6 @@
</q-dialog>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import { useModalStore } from 'src/stores/modalStore';
import Calendar from '../@inputs/Calendar.vue';
import PostalCode from '../@inputs/PostalCode.vue';
import PriceRange from '../@inputs/PriceRange.vue';
import IconCloseModal from '../icons/IconCloseModal.vue';
import IconSearch from '../icons/IconSearch.vue';
export default defineComponent({
name: 'modal-component',
components: {
IconSearch,
IconCloseModal,
PriceRange,
Calendar,
PostalCode,
},
props: {
modalItem: {
type: String,
default: '',
},
},
setup() {
const modalStore = useModalStore();
const modalTextContent = {
isOpenAvailability: {
title: 'Disponibilidad',
subtitle: 'Elige a dónde y cuando quieres enviar el ramo',
},
isOpenFilters: {
title: 'Filtros',
subtitle: 'Personaliza tu búsqueda',
},
};
return {
modalStore,
modalTextContent,
};
},
});
</script>
<style lang="scss">
.modal-container {
& .q-dialog__backdrop {

View File

@ -1,3 +1,70 @@
<script>
import { useQuasar } from "quasar";
import { useFormStore } from "src/stores/forms";
import { useForm } from "vee-validate";
import { defineComponent } from "vue";
import IconArrowRightOne from "src/components/icons/IconArrowRightOne.vue";
import { questionSchema } from "src/utils/zod/schemas/questionSchema";
export default defineComponent({
name: "QuestionForm",
components: { IconArrowRightOne },
setup() {
const $q = useQuasar();
const formStore = useFormStore();
const { handleQuestionData } = formStore;
const { errors, meta, defineField, handleSubmit, handleReset } = useForm({
validationSchema: questionSchema,
initialValues: {
terms: false,
},
});
const [firstName, firstNameAttrs] = defineField("name");
const [secondName, secondNameAttrs] = defineField("surname");
const [email, emailAttrs] = defineField("email");
const [phone, phoneAttrs] = defineField("phone");
const [query, queryAttrs] = defineField("query");
const [message, messageAttrs] = defineField("message");
const [terms, termsAttrs] = defineField("terms");
const onSubmit = handleSubmit((values) => {
console.log(values);
handleQuestionData(values);
handleReset();
if (!terms.value) {
$q.notify({
color: "negative",
message: "Primero tienes que aceptar la licencia y las condiciones",
});
return;
}
});
return {
errors,
firstName,
firstNameAttrs,
secondName,
secondNameAttrs,
email,
emailAttrs,
phone,
phoneAttrs,
query,
queryAttrs,
message,
messageAttrs,
terms,
termsAttrs,
onSubmit,
meta,
};
},
});
</script>
<template>
<form class="question-form-body" @submit.prevent="onSubmit">
<div class="fields">
@ -90,73 +157,6 @@
</form>
</template>
<script>
import { useQuasar } from "quasar";
import { useFormStore } from "src/stores/forms";
import { useForm } from "vee-validate";
import { defineComponent } from "vue";
import IconArrowRightOne from "src/components/icons/IconArrowRightOne.vue";
import { questionSchema } from "src/utils/zod/schemas/questionSchema";
export default defineComponent({
name: "QuestionForm",
components: { IconArrowRightOne },
setup() {
const $q = useQuasar();
const formStore = useFormStore();
const { handleQuestionData } = formStore;
const { errors, meta, defineField, handleSubmit, handleReset } = useForm({
validationSchema: questionSchema,
initialValues: {
terms: false,
},
});
const [firstName, firstNameAttrs] = defineField("name");
const [secondName, secondNameAttrs] = defineField("surname");
const [email, emailAttrs] = defineField("email");
const [phone, phoneAttrs] = defineField("phone");
const [query, queryAttrs] = defineField("query");
const [message, messageAttrs] = defineField("message");
const [terms, termsAttrs] = defineField("terms");
const onSubmit = handleSubmit((values) => {
console.log(values);
handleQuestionData(values);
handleReset();
if (!terms.value) {
$q.notify({
color: "negative",
message: "Primero tienes que aceptar la licencia y las condiciones",
});
return;
}
});
return {
errors,
firstName,
firstNameAttrs,
secondName,
secondNameAttrs,
email,
emailAttrs,
phone,
phoneAttrs,
query,
queryAttrs,
message,
messageAttrs,
terms,
termsAttrs,
onSubmit,
meta,
};
},
});
</script>
<style lang="scss" scoped>
.question-form-body {
display: flex;

View File

@ -1,3 +1,28 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "send-banner",
props: {
leftText: {
type: String,
default: "",
required: true,
},
rightText: {
type: String,
default: "",
required: true,
},
mobileText: {
type: String,
default: "",
required: true,
},
},
});
</script>
<template>
<div class="send-banner-container">
<div class="send-banner-wrapper custom-container">
@ -8,31 +33,6 @@
</div>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'send-banner',
props: {
leftText: {
type: String,
default: '',
required: true,
},
rightText: {
type: String,
default: '',
required: true,
},
mobileText: {
type: String,
default: '',
required: true,
},
},
});
</script>
<style lang="scss" scoped>
.send-banner-container {
display: flex;

View File

@ -150,6 +150,12 @@ 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;
}
}
//! 1440px
@ -291,7 +297,7 @@ p,
color: $title-default;
}
& .pege-subtitle {
& .page-subtitle {
font-family: $font-questrial;
font-size: 1rem;
text-align: center;
@ -313,3 +319,42 @@ body {
margin: 0 auto;
padding: 0 20px;
}
.carousel-content-item .custom-block-content .custom-head-paragraph {
margin-bottom: -14px;
}
//! QUASAR
.q-virtual-scroll__content .q-item .q-item__label {
font-family: $font-questrial;
font-size: $font-14;
color: $text-default;
line-height: 21px;
letter-spacing: 0.28px;
}
.checkout-fields {
& .q-placeholder::placeholder,
& .q-field__label {
font-family: $font-questrial;
font-size: $font-14;
color: $text-default;
line-height: 21px;
letter-spacing: 0.28px;
}
}
.q-carousel__navigation {
@media only screen and (max-width: $med-md) {
display: none;
}
}
.custom-input-el {
& .q-placeholder::placeholder {
font-family: $font-questrial;
color: $text-normal-100 !important;
opacity: 1;
}
}

View File

@ -0,0 +1,35 @@
import { Notify } from "quasar";
export function quasarNotify({ message = "", type, timeout = 1000 }) {
const obj = {
success: () =>
Notify.create({
message,
color: "positive",
position: "top",
icon: "check_circle",
timeout,
}),
warning: () =>
Notify.create({
message,
color: "warning",
position: "top",
icon: "report_problem",
timeout,
}),
erro: () =>
Notify.create({
message,
color: "negative",
position: "top",
icon: "report_problem",
timeout,
}),
};
if (type) {
return obj[type]() || console.error(`Type is invalid! TYPE: ${type}`);
}
console.error("Type is required, success, warning or erro");
}

View File

@ -1,6 +1,3 @@
// This is just an example,
// so you can safely delete all default props below
export default {
failed: "Action failed",
success: "Action was successful",

4
src/i18n/es/index.js Normal file
View File

@ -0,0 +1,4 @@
export default {
failed: "Acción fallida",
success: "La acción se ha realizado correctamente",
};

View File

@ -1,5 +1,9 @@
import enUS from "./en-US";
import es from "./es";
import pt from "./pt";
export default {
"en-US": enUS,
es,
pt,
};

4
src/i18n/pt/index.js Normal file
View File

@ -0,0 +1,4 @@
export default {
failed: "Ação falhou",
success: "Ação foi um sucesso",
};

View File

@ -1,22 +1,4 @@
<template>
<q-layout>
<q-no-ssr>
<header-secondary />
</q-no-ssr>
<mobile-nav />
<q-page-container class="no-padding padding-top more">
<router-view />
</q-page-container>
<reasons-section />
<question-section />
<footer-component />
</q-layout>
</template>
<script lang="js">
<script>
import { defineComponent } from "vue";
import FooterComponent from "src/components/footer/FooterComponent.vue";
@ -46,4 +28,22 @@ export default defineComponent({
});
</script>
<template>
<q-layout>
<q-no-ssr>
<header-secondary />
</q-no-ssr>
<mobile-nav />
<q-page-container class="no-padding padding-top more">
<router-view />
</q-page-container>
<reasons-section />
<question-section />
<footer-component />
</q-layout>
</template>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,32 @@
<script>
import { defineComponent } from "vue";
import { storeToRefs } from "pinia";
import FooterComponent from "src/components/footer/FooterComponent.vue";
import HeaderSecondary from "src/components/header/HeaderSecondary.vue";
import DudasSection from "src/components/sections/DudasSection.vue";
import QuestionSection from "src/components/sections/QuestionSection.vue";
import MobileNav from "src/components/ui/MobileNav.vue";
import { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
name: "ProductLayout",
components: {
HeaderSecondary,
FooterComponent,
QuestionSection,
MobileNav,
DudasSection,
},
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<template>
<q-layout>
<q-no-ssr>
@ -17,33 +46,4 @@
</q-layout>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import FooterComponent from 'src/components/footer/FooterComponent.vue';
import HeaderSecondary from 'src/components/header/HeaderSecondary.vue';
import DudasSection from 'src/components/sections/DudasSection.vue';
import QuestionSection from 'src/components/sections/QuestionSection.vue';
import MobileNav from 'src/components/ui/MobileNav.vue';
import { useMobileStore } from 'src/stores/mobileNav';
export default defineComponent({
name: 'ProductLayout',
components: {
HeaderSecondary,
FooterComponent,
QuestionSection,
MobileNav,
DudasSection,
},
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,25 @@
<script>
import { defineComponent } from "vue";
import { storeToRefs } from "pinia";
import FooterComponent from "src/components/footer/FooterComponent.vue";
import HeaderSecondary from "src/components/header/HeaderSecondary.vue";
import ReasonsSection from "src/components/sections/ReasonsSection.vue";
import MobileNav from "src/components/ui/MobileNav.vue";
import { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
name: "DefaultLayout",
components: { FooterComponent, ReasonsSection, HeaderSecondary, MobileNav },
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<template>
<q-layout>
<q-no-ssr>
@ -15,26 +37,4 @@
</q-layout>
</template>
<script lang="js">
import { defineComponent } from 'vue';
import { storeToRefs } from 'pinia';
import FooterComponent from 'src/components/footer/FooterComponent.vue';
import HeaderSecondary from 'src/components/header/HeaderSecondary.vue';
import ReasonsSection from 'src/components/sections/ReasonsSection.vue';
import MobileNav from 'src/components/ui/MobileNav.vue';
import { useMobileStore } from 'src/stores/mobileNav';
export default defineComponent({
name: 'DefaultLayout',
components: { FooterComponent, ReasonsSection, HeaderSecondary, MobileNav },
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,34 @@
<script>
import { storeToRefs } from "pinia";
import { defineComponent } from "vue";
import FooterComponent from "src/components/footer/FooterComponent.vue";
import HeaderPrimary from "src/components/header/HeaderPrimary.vue";
import InfoSection from "src/components/sections/InfoSection.vue";
import QuestionSection from "src/components/sections/QuestionSection.vue";
import ReasonsSection from "src/components/sections/ReasonsSection.vue";
import MobileNav from "src/components/ui/MobileNav.vue";
import { useMobileStore } from "src/stores/mobileNav";
export default defineComponent({
name: "HomeLayout",
components: {
HeaderPrimary,
QuestionSection,
InfoSection,
ReasonsSection,
FooterComponent,
MobileNav,
},
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<template>
<q-layout>
<q-no-ssr>
@ -17,35 +48,4 @@
</q-layout>
</template>
<script lang="js">
import { storeToRefs } from 'pinia';
import { defineComponent } from 'vue';
import FooterComponent from 'src/components/footer/FooterComponent.vue';
import HeaderPrimary from 'src/components/header/HeaderPrimary.vue';
import InfoSection from 'src/components/sections/InfoSection.vue';
import QuestionSection from 'src/components/sections/QuestionSection.vue';
import ReasonsSection from 'src/components/sections/ReasonsSection.vue';
import MobileNav from 'src/components/ui/MobileNav.vue';
import { useMobileStore } from 'src/stores/mobileNav';
export default defineComponent({
name: 'HomeLayout',
components: {
HeaderPrimary,
QuestionSection,
InfoSection,
ReasonsSection,
FooterComponent,
MobileNav,
},
setup() {
const mobileStore = useMobileStore();
const { isOpenNav } = storeToRefs(mobileStore);
return { isOpenNav };
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,4 +1,43 @@
import { fakerES } from "@faker-js/faker";
export function generateFlowers({ length }) {
const flowersMock = Array.from({ length }, (_, i) => {
const position = fakerES.datatype.boolean() ? i + 1 : undefined;
const flower = {
id: i + 1,
name: fakerES.commerce.productName(),
description: fakerES.commerce.productDescription(),
price: fakerES.commerce.price({
symbol: "€",
min: 20,
max: 200,
dec: 0,
}),
specialPrice: fakerES.commerce.price({
symbol: "€",
min: 20,
max: 60,
dec: 0,
}),
slug: fakerES.commerce.isbn({ separator: "-", variant: 13 }),
category: fakerES.commerce.productMaterial(),
images: Array.from(
{ length: fakerES.number.int({ min: 2, max: 6 }) },
() => fakerES.image.urlPicsumPhotos()
),
featured: fakerES.datatype.boolean(),
};
if (position) {
flower.position = position;
}
return flower;
});
return flowersMock;
}
// console.log(generateFlowers({ length: 100 }));
export const cardMock = Array.from({ length: 8 }, (_, i) => ({
id: i + 1,
@ -7,29 +46,4 @@ export const cardMock = Array.from({ length: 8 }, (_, i) => ({
discount: fakerES.commerce.price({ min: 5, max: 15, dec: 0 }),
isNew: fakerES.datatype.boolean(),
value: fakerES.commerce.price({ min: 20, max: 150 }),
// title: 'Nombre del producto',
// discount: i % 2 === 0 ? '10' : '',
// isNew: i % 2 === 0,
// value: '25,90',
}));
export function generateFlowers({ length }) {
const flowersMock = Array.from({ length }, (_, i) => ({
id: i + 1,
title: fakerES.commerce.productName(),
description: fakerES.commerce.productDescription(),
price: fakerES.commerce.price({
symbol: "€",
min: 20,
max: 200,
dec: 0,
}),
sku: fakerES.commerce.isbn({ separator: "", variant: 13 }),
category: fakerES.commerce.productMaterial(),
images: Array.from({ length: fakerES.number.int({ min: 2, max: 6 }) }, () =>
fakerES.image.urlPicsumPhotos()
),
}));
return flowersMock;
}

View File

@ -1,3 +1,109 @@
<script>
import { fakerES } from "@faker-js/faker";
import { storeToRefs } from "pinia";
import {
defineAsyncComponent,
defineComponent,
onUpdated,
reactive,
ref,
watch,
} from "vue";
import { useRoute } from "vue-router";
import SortSelect from "src/components/@inputs/SortSelect.vue";
import IconArrowCircleFilledRight from "src/components/icons/IconArrowCircleFilledRight.vue";
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 Container from "src/components/ui/Container.vue";
import { useFormStore } from "src/stores/forms";
import { useModalStore } from "src/stores/modalStore";
export default defineComponent({
name: "CategoryPage",
components: {
IconArrowCircleFilledRight,
IconArrowDownWhite,
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")),
SortSelect,
},
setup() {
const modalStore = useModalStore();
const formStore = useFormStore();
const { availability, sortProductFilters } = storeToRefs(formStore);
const monthES = reactive({
0: "Enero",
1: "Febrero",
2: "Marzo",
3: "Abril",
4: "Mayo",
5: "Junio",
6: "Julio",
7: "Agosto",
8: "Septiembre",
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",
};
watch(
() => route.path,
(newPatch) => {
sortProductFilters.value.category = newPatch.split("/")[2];
}
);
onUpdated(() => {
console.groupCollapsed("%c Updated!", "color: green;");
console.log(sortProductFilters.value);
console.groupEnd();
});
function openOrderFilter() {
sortProductFilters.value.isOpenOrderFilter =
!sortProductFilters.value.isOpenOrderFilter;
}
return {
sortProductFilters,
openOrderFilter,
availability,
isOpenOrder,
modalStore,
orderText,
cardsMock,
};
},
});
</script>
<template>
<q-page class="category-container">
<section class="products-section">
@ -109,109 +215,6 @@
</q-page>
</template>
<script>
import { fakerES } from "@faker-js/faker";
import { storeToRefs } from "pinia";
import {
defineAsyncComponent,
defineComponent,
onUpdated,
reactive,
ref,
watch,
} from "vue";
import { useRoute } from "vue-router";
import SortSelect from "src/components/@inputs/SortSelect.vue";
import IconArrowCircleFilledRight from "src/components/icons/IconArrowCircleFilledRight.vue";
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 Container from "src/components/ui/Container.vue";
import { useFormStore } from "src/stores/forms";
import { useModalStore } from "src/stores/modalStore";
export default defineComponent({
name: "CategoryPage",
components: {
IconArrowCircleFilledRight,
IconArrowDownWhite,
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")),
SortSelect,
},
setup() {
const modalStore = useModalStore();
const formStore = useFormStore();
const { availability, sortProductFilters } = storeToRefs(formStore);
const monthES = reactive({
0: "Enero",
1: "Febrero",
2: "Marzo",
3: "Abril",
4: "Mayo",
5: "Junio",
6: "Julio",
7: "Agosto",
8: "Septiembre",
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 }),
}));
const orderText = {
"lowest-price": "menor precio",
"highest-price": "mayor precio",
recommended: "recomendados",
latest: "más recientes",
};
watch(
() => route.path,
(newPatch) => {
sortProductFilters.value.category = newPatch.split("/")[2];
}
);
onUpdated(() => {
console.groupCollapsed("%c Updated!", "color: green;");
console.log(sortProductFilters.value);
console.groupEnd();
});
function openOrderFilter() {
sortProductFilters.value.isOpenOrderFilter =
!sortProductFilters.value.isOpenOrderFilter;
}
return {
sortProductFilters,
openOrderFilter,
availability,
isOpenOrder,
modalStore,
orderText,
cardsMock,
};
},
});
</script>
<style lang="scss" scoped>
.products-section {
display: flex;

View File

@ -1,26 +1,228 @@
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { useForm } from "vee-validate";
import { computed, defineComponent, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import Container from "src/components/ui/Container.vue";
import { useCartStore } from "src/stores/cart";
import { useFormStore } from "src/stores/forms";
import { checkoutSchema } from "src/utils/zod/schemas/checkoutSchema";
export default defineComponent({
name: "CheckoutPage",
components: {
Container,
},
setup() {
/* const t = useI18n();
const content = ref(t("success"));
console.log(content); */
const { push } = useRouter();
const cartStore = useCartStore();
const { cart, cartList, totalPrice, cartLength } = storeToRefs(cartStore);
if (cartLength.value === 0) return push("/");
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 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 checkoutBlock = ref(true);
const onSubmit = handleSubmit((values) => {
handleCheckoutData(values);
stepList.data[2].active = true;
checkoutBlock.value = false;
resetForm();
});
const handleClickStep = (value) => {
stepActive["data"] = value;
};
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 {
handleClickStep,
stepsFormated,
onSubmit,
stepList,
provinceOptions,
totalPrice,
cartList,
step: ref(1),
cart,
checkoutBlock,
meta,
errors,
name,
nameAttrs,
surname,
surnameAttrs,
address,
addressAttrs,
postalCode,
postalCodeAttrs,
phone,
phoneAttrs,
city,
cityAttrs,
province,
provinceAttrs,
senderName,
senderNameAttrs,
senderCifNif,
senderCifNifAttrs,
senderEmail,
senderEmailAttrs,
senderPhone,
senderPhoneAttrs,
senderNotes,
senderNotesAttrs,
terms,
termsAttrs,
paymentMethod,
paymentMethodAttrs,
};
},
});
</script>
<template>
<q-page class="checkout-page">
<Container tag="section">
<header class="header-title" :class="!checkoutBlock && 'success'">
<h1 class="pege-title" v-if="checkoutBlock">
¿A quién y dónde lo entregamos?
{{ checkoutBlock && "¿A quién y dónde lo entregamos?" }}
{{ !checkoutBlock && "¡Muchas gracias Jerom!" }}
</h1>
<h1 class="pege-title" v-if="!checkoutBlock">¡Muchas gracias Jerom!</h1>
<p class="pege-subtitle checkout" v-if="checkoutBlock">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<p class="pege-subtitle checkout" v-if="!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 class="page-subtitle checkout" v-if="checkoutBlock">
{{
checkoutBlock &&
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
}}
{{
!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>
</header>
<div class="checkout-container">
<div class="checkout-steps">
<div
v-for="({ active, description, name, value }, i) in stepsFormated()"
v-for="({ active, description, name, value }, i) in stepsFormated"
class="step-item-container"
:key="i"
>
@ -116,7 +318,7 @@
placeholder="Código postal*"
name="postalCode"
type="text"
mask="#####-###"
mask="#####"
v-model="postalCode"
v-bind:="postalCodeAttrs"
:error="!!errors.postalCode"
@ -135,11 +337,7 @@
:options="provinceOptions"
option-value="code"
option-label="name"
:label="
!province
? 'Complete la dirección y el código postal'
: 'Provincia*'
"
label="Provincia*"
stack-label
map-options
emit-value
@ -149,7 +347,7 @@
<div class="field-control field-select">
<q-input
placeholder="Ciudade*"
placeholder="Ciudad*"
name="city"
type="text"
v-model="city"
@ -282,11 +480,11 @@
<ul class="checkout-summary-list">
<li
class="checkout-summary-item"
v-for="({ title, price }, index) in cart"
v-for="({ title, price, quantity }, index) in cartList"
:key="index"
>
<p>
{{ title }}
{{ title }} ({{ quantity }})
<span>{{ price }}</span>
</p>
</li>
@ -309,7 +507,7 @@
</header>
<div class="checkout-payment-body">
<q-radio
<!-- <q-radio
v-model="paymentMethod"
v-bind="paymentMethodAttrs"
val="credit"
@ -321,7 +519,7 @@
<IconMaster /><IconVisa /> <IconAny /> <IconExpress />
</span>
</p>
</q-radio>
</q-radio> -->
<q-radio
v-model="paymentMethod"
@ -359,7 +557,7 @@
<div class="checkout-success-content">
<ul class="checkout-success-list">
<li
v-for="({ title, price }, index) in cart"
v-for="({ title, price, quantity }, index) in cartList"
:key="index"
class="checkout-success-item"
>
@ -370,7 +568,9 @@
alt="product"
class="checkout-product-img"
/>
<p class="checkout-product-title">{{ title }}</p>
<p class="checkout-product-title">
{{ title }} ({{ quantity }})
</p>
</div>
<p class="checkout-product-price">
@ -383,7 +583,9 @@
<footer class="checkout-success-footer">
<p class="checkout-success-paragraph">Total</p>
<p class="checkout-success-paragraph">{{ totalPrice }}.00</p>
<p class="checkout-success-paragraph">
{{ totalPrice?.toFixed(2) }}
</p>
</footer>
</div>
</div>
@ -392,230 +594,32 @@
</q-page>
</template>
<script>
import { toTypedSchema } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { useForm } from "vee-validate";
import { defineComponent, reactive, ref } from "vue";
import IconAny from "src/components/icons/credit-flags/IconAny.vue";
import IconExpress from "src/components/icons/credit-flags/IconExpress.vue";
import IconMaster from "src/components/icons/credit-flags/IconMaster.vue";
import IconVisa from "src/components/icons/credit-flags/IconVisa.vue";
import Container from "src/components/ui/Container.vue";
import { useCartStore } from "src/stores/cart";
import { useFormStore } from "src/stores/forms";
import { checkoutSchema } from "src/utils/zod/schemas/checkoutSchema";
export default defineComponent({
name: "CheckoutPage",
components: {
Container,
IconAny,
IconVisa,
IconExpress,
IconMaster,
},
setup() {
const cartStore = useCartStore();
const { cart, totalPrice } = storeToRefs(cartStore);
const formStore = useFormStore();
const { handleCheckoutData } = formStore;
const { meta, errors, handleSubmit, defineField, resetForm } = useForm({
validationSchema: toTypedSchema(checkoutSchema),
initialValues: {
paymentMethod: "credit",
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 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 checkoutBlock = ref(true);
const onSubmit = handleSubmit((values) => {
handleCheckoutData(values);
stepList.data[2].active = true;
checkoutBlock.value = false;
resetForm();
});
const handleClickStep = (value) => {
stepActive["data"] = value;
};
const stepsFormated = () => {
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 {
handleClickStep,
stepsFormated,
onSubmit,
stepList,
provinceOptions,
totalPrice,
step: ref(1),
cart,
checkoutBlock,
meta,
errors,
name,
nameAttrs,
surname,
surnameAttrs,
address,
addressAttrs,
postalCode,
postalCodeAttrs,
phone,
phoneAttrs,
city,
cityAttrs,
province,
provinceAttrs,
senderName,
senderNameAttrs,
senderCifNif,
senderCifNifAttrs,
senderEmail,
senderEmailAttrs,
senderPhone,
senderPhoneAttrs,
senderNotes,
senderNotesAttrs,
terms,
termsAttrs,
paymentMethod,
paymentMethodAttrs,
};
},
});
</script>
<style lang="scss" scoped>
.checkout-page {
.checkout-steps {
& .checkout-steps {
display: flex;
justify-content: center;
align-items: center;
}
.step-item-container {
& .step-item-container {
min-width: 200px;
}
.border-step {
& .border-step {
width: 90px;
height: 1px;
background-color: $primary-dark;
}
.circle-step-container {
& .circle-step-container {
display: grid;
justify-content: center;
align-items: center;
grid-template-columns: 1fr auto 1fr;
}
.circle-step {
& .circle-step {
width: 56px;
height: 56px;
border: 1px solid $primary-dark;
@ -637,7 +641,7 @@ export default defineComponent({
}
}
.step-content {
& .step-content {
display: flex;
flex-direction: column;
align-items: center;
@ -659,7 +663,7 @@ export default defineComponent({
& .checkout-content {
width: min(100%, 1144px);
margin: 50px auto 0;
margin: 50px auto calc(146px - 72px);
display: flex;
flex-wrap: wrap;
gap: 20px;
@ -670,10 +674,12 @@ export default defineComponent({
width: 100%;
margin-bottom: 21px;
border-radius: 5px;
h3 {
& h3 {
color: $text-default;
font-weight: 600;
font-size: 0.875rem;
line-height: 1.5;
line-height: 21px;
letter-spacing: 0.28px;
}
@media only screen and (max-width: $med-lg) {
@ -909,7 +915,7 @@ export default defineComponent({
}
}
.form-fields-container {
& .form-fields-container {
display: flex;
flex-wrap: wrap;
&.delivery {
@ -942,15 +948,21 @@ export default defineComponent({
label {
padding-bottom: 10px;
}
.q-field__control {
& .q-field__control {
background-color: #fff;
height: 40px;
border: 1px solid $primary-light;
input {
padding: 0px 30px;
& input {
padding: 0px 0px 0px 20px;
font-family: $font-questrial;
color: $text-default !important;
}
& select {
font-family: $font-questrial;
color: $text-default !important;
}
&.text-negative {
border-color: $negative;
}

View File

@ -1,3 +1,15 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "ContactaPage",
components: {},
setup() {
return {};
},
});
</script>
<template>
<q-page class="">
<p>Contacta</p>
@ -6,16 +18,4 @@
</q-page>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ContactaPage',
components: {},
setup() {
return {};
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,11 +1,46 @@
<script>
import { defineComponent, onMounted, ref } from "vue";
import { useRouter } from "vue-router";
export default defineComponent({
name: "ErrorNotFound",
setup() {
const counter = ref(10);
const { push } = useRouter();
function startCountdown() {
// Cria um intervalo que executa a cada segundo
const interval = setInterval(() => {
// Decrementa o valor de count
counter.value--;
// Se o valor de count for zero, para o intervalo
if (counter.value === 1) {
clearInterval(interval);
push("/");
}
}, 1000);
}
// Chama a função para iniciar o contador quando o componente for montado
onMounted(startCountdown);
return {
counter,
};
},
});
</script>
<template>
<div
class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center"
class="not-found fullscreen text-white text-center q-pa-md flex flex-center"
>
<div>
<div style="font-size: 30vh">404</div>
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div>
<div class="text-h2" style="opacity: 0.4" :key="counter">
Redirigiendo a la home en... {{ counter }}
</div>
<q-btn
class="q-mt-xl"
@ -20,10 +55,8 @@
</div>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'ErrorNotFound',
});
</script>
<style lang="scss" scoped>
.not-found {
background-color: $primary;
}
</style>

21
src/pages/ExamplePage.vue Normal file
View File

@ -0,0 +1,21 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "ExamplePage",
components: {},
setup() {
return {};
},
});
</script>
<template>
<div>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quam rerum omnis
repellat. Harum ducimus nulla repellendus neque officia eveniet corporis
odio sequi animi ut, non incidunt est error esse aperiam?
</div>
</template>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,15 @@
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "FaqPage",
components: {},
setup() {
return {};
},
});
</script>
<template>
<q-page class="">
<p>Faq</p>
@ -6,16 +18,4 @@
</q-page>
</template>
<script lang="js">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'FaqPage',
components: {},
setup() {
return {};
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -1,3 +1,70 @@
<script>
import { storeToRefs } from "pinia";
import { defineAsyncComponent, defineComponent, onBeforeMount, ref } from "vue";
import { apiBack } from "src/boot/axios";
import Container from "src/components/ui/Container.vue";
import { cardMock } from "src/mock/cards";
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,
}),
Container,
},
setup() {
const mobileStore = useMobileStore();
const { isCarouselVisible, isOpenNav, screenWidth } =
storeToRefs(mobileStore);
const slidesContent = [
"assets/1.jpg",
"assets/2.jpg",
"assets/3.jpg",
"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,
};
},
});
</script>
<template>
<q-page>
<q-no-ssr>
@ -18,19 +85,19 @@
</header>
<div class="products-body">
<Container cardContainer>
<Card
v-for="(
{ imgSrc, discount, isNew, title, value, id }, i
) in cardMock"
:productValue="value"
:productName="title"
:discount="discount"
:imgSrc="imgSrc"
:isNew="isNew"
:key="i"
:id="id"
/>
<Container class="row-md" cardContainer>
<template
v-for="({ id, slug, name, price, images }, i) in dataProduct"
>
<Card
v-if="i < 8"
:id="slug"
:productName="name"
:productValue="price"
:imgSrc="images[0]"
:key="id"
/>
</template>
</Container>
<RouterLink class="btn rounded outlined" to="/">
@ -53,23 +120,6 @@
</header>
<div class="products-selection-body">
<!-- <HorizontalCarousel>
<SwiperSlideOne
v-for="{ id, discount, isNew, value, title, imgSrc } in cardMock"
:key="id"
>
<Card
:id="id"
:key="id"
:productValue="value"
:productName="title"
:discount="discount"
:imgSrc="imgSrc"
:isNew="isNew"
/>
</SwiperSlideOne>
</HorizontalCarousel> -->
<q-no-ssr>
<Swiper>
<swiper-slide
@ -100,55 +150,6 @@
</q-page>
</template>
<script lang="js">
import { storeToRefs } from 'pinia';
import { defineAsyncComponent, defineComponent, ref } from 'vue';
import Container from 'src/components/ui/Container.vue';
import { cardMock } from 'src/mock/cards';
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(() => import('src/components/ui/Card.vue')),
Container,
},
setup() {
const mobileStore = useMobileStore();
const { isCarouselVisible, isOpenNav, screenWidth } =
storeToRefs(mobileStore);
const slidesContent = [
'assets/1.jpg',
'assets/2.jpg',
'assets/3.jpg',
'assets/4.jpg',
'assets/5.jpg',
];
const data = ref(null);
return {
isCarouselVisible,
slidesContent,
screenWidth,
isOpenNav,
cardMock,
data,
};
},
});
</script>
<style lang="scss" scoped>
.home-carousel {
margin-bottom: 132px;

View File

@ -1,28 +1,148 @@
<script>
import { fakerES } from "@faker-js/faker";
import { storeToRefs } from "pinia";
import { useMeta } from "quasar";
import { useForm } from "vee-validate";
import { defineComponent, onBeforeMount, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";
import IconArrowCircleFilledLeft from "components/icons/IconArrowCircleFilledLeft.vue";
import IconArrowCircleFilledRight from "components/icons/IconArrowCircleFilledRight.vue";
import IconPencilGreen from "components/icons/IconPencilGreen.vue";
import IconEmail from "components/icons/social/IconEmail.vue";
import IconLinkedin from "components/icons/social/IconLinkedin.vue";
import IconShare from "components/icons/social/IconShare.vue";
import IconTwitter from "components/icons/social/IconTwitter.vue";
import IconWhatsapp from "components/icons/social/IconWhatsapp.vue";
import ProductCarousel from "components/quasar-components/carousel/ProductCarousel.vue";
import DudasSection from "components/sections/DudasSection.vue";
import Card from "components/ui/Card.vue";
import Container from "components/ui/Container.vue";
import { dedicationSchema } from "src/utils/zod/schemas";
import { useCartStore } from "stores/cart";
export default defineComponent({
name: "ProductPage",
components: {
IconPencilGreen,
IconWhatsapp,
IconLinkedin,
IconTwitter,
IconShare,
IconEmail,
DudasSection,
Container,
Card,
IconArrowCircleFilledRight,
IconArrowCircleFilledLeft,
ProductCarousel,
},
setup() {
const route = useRoute();
const cartStore = useCartStore();
const { addToCart, getProduct } = 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 }),
}));
watch(currentProduct.value, (newValue) => {
useMeta(() => {
return {
title: `${newValue.value?.title}`,
titleTemplate: (title) => `${title} - FloraNet`,
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)",
},
},
};
});
});
onBeforeMount(() => {
getProduct(+route.params.id);
});
watch(
() => route.params.id,
(newId) => {
getProduct(+newId);
}
);
const currentData = reactive({});
watch(currentProduct.value, (newData) => {
if (newData.value) {
const { id, ...newDataWhithoutId } = newData.value;
currentData.value = {
...newDataWhithoutId,
productId: +route.params.id,
};
}
});
const { handleSubmit, defineField, handleReset } = useForm({
validationSchema: dedicationSchema,
});
const [dedication, dedicationAttrs] = defineField("dedication");
const onSubmit = handleSubmit(() => {
addToCart(currentData.value, dedication);
handleReset();
});
return {
cardMock,
slide: ref(1),
fullscreen: ref(false),
dedication,
dedicationAttrs,
onSubmit,
addCartLoadingBtn,
prevProduct,
currentProduct,
nextProduct,
currentData,
};
},
});
</script>
<template>
<q-page>
<!-- <p>{{ $route.params.id }}</p> -->
<Container class="product-container" tag="section">
<ProductCarousel>
<q-carousel-slide
:name="1"
img-src="https://cdn.quasar.dev/img/mountains.jpg"
class="product-gallery-item"
/>
<q-carousel-slide
:name="2"
img-src="https://cdn.quasar.dev/img/parallax1.jpg"
class="product-gallery-item"
/>
<q-carousel-slide
:name="3"
img-src="https://cdn.quasar.dev/img/parallax2.jpg"
class="product-gallery-item"
/>
<q-carousel-slide
:name="4"
img-src="https://cdn.quasar.dev/img/quasar.jpg"
v-for="(img, i) in currentProduct.value?.images"
:img-src="img"
class="product-gallery-item"
:name="i"
:key="i"
/>
</ProductCarousel>
@ -33,8 +153,6 @@
<q-skeleton type="rect" v-if="!currentProduct.value?.title" />
</h3>
<!-- <div>{{ currentData.value }}</div> -->
<div class="product-header-block">
<p class="product-content-paragraph">
SKU:
@ -143,6 +261,7 @@
class="btn outlined rounded sm-btn product-pag-item product-prev-btn"
:to="`${+$route.params.id - 1}`"
v-if="+$route.params.id > 1"
@click="currentProduct.value = undefined"
>
<IconArrowCircleFilledLeft />
@ -161,6 +280,8 @@
color="white"
class="btn outlined rounded sm-btn product-pag-item product-next-btn"
:to="`${+$route.params.id + 1}`"
v-if="nextProduct.value?.id"
@click="currentProduct.value = undefined"
>
<div class="btn-pag-paragraphs">
<p class="btn-paragraph-top green-text">Siguiente producto</p>
@ -212,128 +333,6 @@
</q-page>
</template>
<script>
import { fakerES } from "@faker-js/faker";
import { useMeta } from "quasar";
import { useForm } from "vee-validate";
import { defineComponent, onBeforeMount, ref, watch } from "vue";
import { useRoute } from "vue-router";
import IconArrowCircleFilledLeft from "components/icons/IconArrowCircleFilledLeft.vue";
import IconArrowCircleFilledRight from "components/icons/IconArrowCircleFilledRight.vue";
import IconPencilGreen from "components/icons/IconPencilGreen.vue";
import IconEmail from "components/icons/social/IconEmail.vue";
import IconLinkedin from "components/icons/social/IconLinkedin.vue";
import IconShare from "components/icons/social/IconShare.vue";
import IconTwitter from "components/icons/social/IconTwitter.vue";
import IconWhatsapp from "components/icons/social/IconWhatsapp.vue";
import ProductCarousel from "components/quasar-components/carousel/ProductCarousel.vue";
import DudasSection from "components/sections/DudasSection.vue";
import Card from "components/ui/Card.vue";
import Container from "components/ui/Container.vue";
import { storeToRefs } from "pinia";
import { dedicationSchema } from "src/utils/zod/schemas";
import { useCartStore } from "stores/cart";
export default defineComponent({
name: "ProductPage",
components: {
IconPencilGreen,
IconWhatsapp,
IconLinkedin,
IconTwitter,
IconShare,
IconEmail,
DudasSection,
Container,
Card,
IconArrowCircleFilledRight,
IconArrowCircleFilledLeft,
ProductCarousel,
},
setup() {
const route = useRoute();
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 }),
}));
useMeta({
title: "Product",
titleTemplate: (title) => `${title} ${route.params.id} - FloraNet`,
meta: {
description: { name: "description", content: "Page 1" },
keywords: { name: "keywords", content: "Quasar website" },
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 cartStore = useCartStore();
const { addToCart, getProduct } = cartStore;
const { prevProduct, currentProduct, nextProduct, addCartLoadingBtn } =
storeToRefs(cartStore);
onBeforeMount(() => {
getProduct(+route.params.id);
});
watch(
() => route.params.id,
(newId) => {
getProduct(+newId);
}
);
const currentData = ref({});
watch(currentProduct.value, (newData) => {
const { id, ...newDataWhithoutId } = newData.value;
currentData.value = { ...newDataWhithoutId, productId: +route.params.id };
});
const { handleSubmit, defineField, handleReset } = useForm({
validationSchema: dedicationSchema,
});
const [dedication, dedicationAttrs] = defineField("dedication");
const onSubmit = handleSubmit(() => {
addToCart(currentData.value, dedication);
handleReset();
});
return {
cardMock,
slide: ref(1),
fullscreen: ref(false),
dedication,
dedicationAttrs,
onSubmit,
addCartLoadingBtn,
prevProduct,
currentProduct,
nextProduct,
currentData,
};
},
});
</script>
<style lang="scss">
.product-container {
display: flex;

View File

@ -62,8 +62,14 @@ const routes = [
name: "Contacta",
component: () => import("pages/ContactaPage.vue"),
},
{
path: "/example",
name: "Example",
component: () => import("pages/ExamplePage.vue"),
},
{
path: "/:catchAll(.*)*",
name: "NotFound",
component: () => import("pages/ErrorNotFound.vue"),
},
];

View File

@ -1397,54 +1397,42 @@
],
"cart": [
{
"title": "Increible Algodón Silla",
"description": "Ergonomic executive chair upholstered in bonded black leather and PVC padded seat and back for all-day comfort and support",
"price": "€198",
"sku": "9781774532096",
"category": "Acero",
"title": "Guapa Algodón Ordenador",
"description": "Boston's most advanced compression wear technology increases muscle oxygenation, stabilizes active muscles",
"price": "€150",
"sku": "9781616149024",
"category": "Plástico",
"images": [
"https://picsum.photos/seed/jnIFY2/640/480",
"https://picsum.photos/seed/sMMlbf6zc8/640/480",
"https://picsum.photos/seed/NpRZteKf/640/480",
"https://picsum.photos/seed/cQ7Oei/640/480",
"https://picsum.photos/seed/wUVXjS1zi/640/480",
"https://picsum.photos/seed/ym8fuWY8/640/480"
"https://picsum.photos/seed/dyj4Y/640/480",
"https://picsum.photos/seed/vQWtap1t/640/480"
],
"productId": 10,
"productId": 3,
"id": 1
},
{
"title": "Increible Algodón Silla",
"description": "Ergonomic executive chair upholstered in bonded black leather and PVC padded seat and back for all-day comfort and support",
"price": "€198",
"sku": "9781774532096",
"category": "Acero",
"title": "Inteligente Acero Pantalones",
"description": "Carbonite web goalkeeper gloves are ergonomically designed to give easy fit",
"price": "€24",
"sku": "9780350182908",
"category": "Hormigon",
"images": [
"https://picsum.photos/seed/jnIFY2/640/480",
"https://picsum.photos/seed/sMMlbf6zc8/640/480",
"https://picsum.photos/seed/NpRZteKf/640/480",
"https://picsum.photos/seed/cQ7Oei/640/480",
"https://picsum.photos/seed/wUVXjS1zi/640/480",
"https://picsum.photos/seed/ym8fuWY8/640/480"
"https://picsum.photos/seed/c3QvIh5QR/640/480",
"https://picsum.photos/seed/NZWI0TRX3E/640/480"
],
"productId": 10,
"productId": 1,
"id": 2
},
{
"title": "Increible Algodón Silla",
"description": "Ergonomic executive chair upholstered in bonded black leather and PVC padded seat and back for all-day comfort and support",
"price": "€198",
"sku": "9781774532096",
"category": "Acero",
"title": "Inteligente Acero Pantalones",
"description": "Carbonite web goalkeeper gloves are ergonomically designed to give easy fit",
"price": "€24",
"sku": "9780350182908",
"category": "Hormigon",
"images": [
"https://picsum.photos/seed/jnIFY2/640/480",
"https://picsum.photos/seed/sMMlbf6zc8/640/480",
"https://picsum.photos/seed/NpRZteKf/640/480",
"https://picsum.photos/seed/cQ7Oei/640/480",
"https://picsum.photos/seed/wUVXjS1zi/640/480",
"https://picsum.photos/seed/ym8fuWY8/640/480"
"https://picsum.photos/seed/c3QvIh5QR/640/480",
"https://picsum.photos/seed/NZWI0TRX3E/640/480"
],
"productId": 10,
"productId": 1,
"id": 3
},
{
@ -1461,43 +1449,57 @@
"id": 4
},
{
"title": "Inteligente Acero Pantalones",
"description": "Carbonite web goalkeeper gloves are ergonomically designed to give easy fit",
"price": "€24",
"sku": "9780350182908",
"category": "Hormigon",
"title": "Pequeño Ladrillo Pollo",
"description": "The beautiful range of Apple Naturalé that has an exciting mix of natural ingredients. With the Goodness of 100% Natural Ingredients",
"price": "€195",
"sku": "9781027438533",
"category": "Algodón",
"images": [
"https://picsum.photos/seed/c3QvIh5QR/640/480",
"https://picsum.photos/seed/NZWI0TRX3E/640/480"
"https://picsum.photos/seed/akHdlbK3/640/480",
"https://picsum.photos/seed/KSxDr8aQqe/640/480",
"https://picsum.photos/seed/e3x6PdCNg/640/480"
],
"productId": 1,
"productId": 2,
"id": 5
},
{
"title": "Inteligente Acero Pantalones",
"description": "Carbonite web goalkeeper gloves are ergonomically designed to give easy fit",
"price": "€24",
"sku": "9780350182908",
"category": "Hormigon",
"title": "Guapa Algodón Ordenador",
"description": "Boston's most advanced compression wear technology increases muscle oxygenation, stabilizes active muscles",
"price": "€150",
"sku": "9781616149024",
"category": "Plástico",
"images": [
"https://picsum.photos/seed/c3QvIh5QR/640/480",
"https://picsum.photos/seed/NZWI0TRX3E/640/480"
"https://picsum.photos/seed/dyj4Y/640/480",
"https://picsum.photos/seed/vQWtap1t/640/480"
],
"productId": 1,
"productId": 3,
"id": 6
},
{
"title": "Inteligente Acero Pantalones",
"description": "Carbonite web goalkeeper gloves are ergonomically designed to give easy fit",
"price": "€24",
"sku": "9780350182908",
"category": "Hormigon",
"title": "Guapa Algodón Ordenador",
"description": "Boston's most advanced compression wear technology increases muscle oxygenation, stabilizes active muscles",
"price": "€150",
"sku": "9781616149024",
"category": "Plástico",
"images": [
"https://picsum.photos/seed/c3QvIh5QR/640/480",
"https://picsum.photos/seed/NZWI0TRX3E/640/480"
"https://picsum.photos/seed/dyj4Y/640/480",
"https://picsum.photos/seed/vQWtap1t/640/480"
],
"productId": 1,
"productId": 3,
"id": 7
},
{
"title": "Guapa Algodón Ordenador",
"description": "Boston's most advanced compression wear technology increases muscle oxygenation, stabilizes active muscles",
"price": "€150",
"sku": "9781616149024",
"category": "Plástico",
"images": [
"https://picsum.photos/seed/dyj4Y/640/480",
"https://picsum.photos/seed/vQWtap1t/640/480"
],
"productId": 3,
"id": 8
}
]
}

View File

@ -0,0 +1,3 @@
{
"/jsonServer/*": "/$1"
}

View File

@ -1,9 +1,12 @@
import { defineStore } from "pinia";
import { api } from "src/boot/axios";
import { computed, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { api } from "src/boot/axios";
export const useCartStore = defineStore("cart", () => {
const cart = ref([]);
const cartList = ref([]);
const dedicationTxt = ref("");
const prevProduct = reactive({});
const currentProduct = reactive({});
@ -18,6 +21,7 @@ export const useCartStore = defineStore("cart", () => {
}
}, 0);
});
const { push } = useRouter();
/**
*
@ -27,6 +31,22 @@ export const useCartStore = defineStore("cart", () => {
async function getCart({ debug }) {
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) {
@ -35,7 +55,7 @@ export const useCartStore = defineStore("cart", () => {
console.groupEnd();
}
} catch (err) {
/* throw */ new Error(`FATAL ERROR ::: ${err}`);
new Error(`FATAL ERROR ::: ${err}`);
}
}
getCart({ debug: true });
@ -49,24 +69,38 @@ export const useCartStore = defineStore("cart", () => {
async function getProduct(id) {
if (id) {
try {
const { data } = await api.get(`flowers/${id}`);
currentProduct.value = data;
const { data: dataNext } = await api.get(`flowers/${id + 1}`);
if (dataNext) {
nextProduct.value = dataNext;
}
const promises = [
api.get(`flowers/${id - 1}`),
api.get(`flowers/${id}`),
api.get(`flowers/${id + 1}`),
];
const results = await Promise.allSettled(promises);
const [prev, current, next] = results.map((res) => {
const result = {
fulfilled: res.value?.data,
rejected: res.reason,
};
return result[res.status];
});
prevProduct.value = prev;
currentProduct.value = current;
nextProduct.value = next;
console.groupCollapsed("%c Produtos recebido!", "color: green;");
if (id - 1 > 0) {
const { data: dataPrev } = await api.get(`flowers/${id - 1}`);
prevProduct.value = dataPrev;
console.table(prevProduct.value);
}
console.time();
console.table(prevProduct.value);
console.table(currentProduct.value);
console.table(nextProduct.value);
console.timeEnd();
console.groupEnd();
if (currentProduct.value.response?.status === 404) {
push({ name: "NotFound" });
}
} catch (err) {
new Error(`FATAL ERROR ::: ${err}`);
console.error(`FATAL ERROR ::: ${err}`);
}
}
}
@ -79,23 +113,25 @@ export const useCartStore = defineStore("cart", () => {
try {
await api.post("cart", product);
addCartLoadingBtn.value = false;
// push("/checkout");
console.groupCollapsed("%c Adicionado com sucesso!", "color: green");
console.table(cart.value);
console.groupEnd();
} catch (err) {
addCartLoadingBtn.value = false;
new Error(`FATAL ERROR ::: ${err}`);
}
console.groupCollapsed("%c Adicionado com sucesso!", "color: green");
console.table(cart.value);
console.groupEnd();
}
function removeFromCart(id) {
cart.value = cart.value.filter((p) => p.id !== id);
cart.value = cart.value.filter((p) => id !== p.id);
api.delete(`cart/${id}`);
}
return {
cart,
cartList,
totalPrice,
dedicationTxt,
cartLength,

View File

@ -8,5 +8,5 @@ export const postalCode = z
const valWithoutHifen = val.replaceAll("-", "");
const valLength = valWithoutHifen.length;
return valLength === 8 && valLength >= 1;
}, "El código postal debe tener 8 caracteres numéricos válidos");
return valLength === 5 && valLength >= 1;
}, "El código postal debe tener 5 caracteres numéricos válidos");

View File

@ -29,7 +29,7 @@ const checkoutObjVal = {
senderPhone: z.string().refine(handlePhoneVal, M.phoneMessage),
senderNotes: z.string(),
paymentMethod: z.enum(["credit", "stripe"], {
required_error: "Seleccione uno de los métodos de pago",
required_error: "Seleccione uno de los métodos de pago!",
}),
terms: z.boolean().refine((val) => {
return val === true;