From 3ca95603131640404fd6c2d6bd9c6e195ce141bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaume=20Sol=C3=ADs?= Date: Wed, 24 Jan 2024 19:52:24 +0100 Subject: [PATCH] =?UTF-8?q?cambios=20programaci=C3=B3n=20semana=2022?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 12 +- api/controller/product.controller.js | 133 +++--- api/db/db.js | 34 +- api/index.js | 2 +- index.html | 2 +- quasar.config.js | 4 +- src/boot/stripe.js | 14 +- src/components/@inputs/Calendar.vue | 78 ++-- src/components/footer/FooterComponent.vue | 6 - src/components/header/HeaderPrimary.vue | 4 +- src/components/header/UserArea.vue | 16 +- .../carousel/VerticalCarouselImgs.vue | 8 +- src/components/sections/QuestionSection.vue | 2 - .../stripe/StripeCheckoutComponent.vue | 65 +++ src/components/ui/Card.vue | 24 +- src/components/ui/Modal.vue | 4 +- src/constants/date.js | 7 + src/functions/invertDate.js | 7 + src/functions/quasarNotify.js | 12 +- src/hooks/useCheckoutForm.js | 192 +++++++++ src/hooks/useLocalStorage.js | 25 ++ src/hooks/usePostalCalendar.js | 136 ++++--- src/hooks/useProductPage.js | 51 +++ src/layouts/DefaultLayout.vue | 8 +- src/mock/cards.js | 2 +- src/pages/CategoryPage.vue | 48 +-- src/pages/CheckoutPage.vue | 292 +++++-------- src/pages/ErrorNotFound.vue | 4 - src/pages/HomePage.vue | 36 +- src/pages/ProductPage.vue | 202 ++++----- src/router/routes.js | 2 +- src/stores/cart.js | 384 +++++++++++------- src/utils/zod/schemas/availabilitySchema.js | 12 +- 33 files changed, 1089 insertions(+), 739 deletions(-) create mode 100644 src/components/stripe/StripeCheckoutComponent.vue create mode 100644 src/constants/date.js create mode 100644 src/functions/invertDate.js create mode 100644 src/hooks/useCheckoutForm.js create mode 100644 src/hooks/useLocalStorage.js create mode 100644 src/hooks/useProductPage.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 4ebbc22..2c9288b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,13 @@ "terminal.integrated.cursorStyleInactive": "line", "workbench.iconTheme": "material-icon-theme", "workbench.colorTheme": "Default Dark+", + "editor.codeActionsOnSave": [ + "source.addMissingImports", + "source.organizeImports", + "source.fixAll.eslint", + "source.fixAll.stylelint" + ], + "files.exclude": { "**/.git": true, "**/.svn": true, @@ -46,7 +53,6 @@ "git.confirmSync": false, "javascript.updateImportsOnFileMove.enabled": "always", "console-ninja.featureSet": "Community", - "liveServer.settings.donotShowInfoMsg": true, "typescript.updateImportsOnFileMove.enabled": "always", "editor.cursorSmoothCaretAnimation": "on", "editor.fontLigatures": true, @@ -60,9 +66,7 @@ "source.organizeImports", "source.fixAll.eslint" ], - "liveServer.settings.donotVerifyTags": true, "gitlens.gitCommands.skipConfirmations": ["fetch:command", "switch:command"], - "symbols.hidesExplorerArrows": false, "diffEditor.ignoreTrimWhitespace": false, "svg.preview.mode": "svg", "[svg]": { @@ -72,7 +76,7 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "workbench.tree.indent": 16, - "window.zoomLevel": 0, + "window.zoomLevel": -1, "git.ignoreRebaseWarning": true, "editor.largeFileOptimizations": false, "[javascript]": { diff --git a/api/controller/product.controller.js b/api/controller/product.controller.js index 72f0471..69a9b67 100644 --- a/api/controller/product.controller.js +++ b/api/controller/product.controller.js @@ -5,45 +5,34 @@ const productsJson = require("./products.json") class ProductController { async findAll(req, res) { - - const _products = await db.getProducts(); - const mapedProducts = await _products[0].map(function (item) { - return { - id: item.id, - name: item.name, - description: item.description, - type: item.type, - price: item.price, - specialPrice: item.specialPrice, - isNew: item.isNew, - slug: item.slug, - category: item.category, - postalCode: item.postalCode, - dateExpired: item.dateExpired, - images: [item.image], - featured: item.featured, - } - }) - - //Gerar produtos fake - /* const fakeProducts = generateFlowers(50) - console.log(fakeProducts); */ const params = req.query; - let productsFilter = mapedProducts - console.log(params); + const _products = await db.getProducts(params.dateExpired, params.postalCode); + let productsFilter = _products[0]; - if (Number(params.featured)) { - productsFilter = productsFilter.filter(item => item.featured === Number(params.featured)) + if (Number(params.recommend)) { + productsFilter = productsFilter.filter(item => item.recommend == Number(params.recommend)) } - if (params.category) { - productsFilter = productsFilter.filter(item => item.category === Number(params.category)) + if (params.type) { + productsFilter = productsFilter.filter(item => item.type === params.type) } - if (params.postalCode) { + /*if (params.postalCode) { productsFilter = productsFilter.filter(item => item.postalCode === params.postalCode) } + if (params.dateExpired) { + const dateSearch = new Date(params.dateExpired); + productsFilter = productsFilter.filter(item => { + const dateProduct = new Date(item.dateExpired); + if (dateProduct >= dateSearch) { + return item + } + }) + }*/ + console.log(productsFilter.length); + + if (params.minPrice && !params.maxPrice) { productsFilter = productsFilter.filter(item => { - const price = Number(item.price.replace(/€/g, '')) + const price = Number(item.price) if (price >= Number(params.minPrice)) { return item } @@ -51,7 +40,7 @@ class ProductController { } if (params.maxPrice && !params.minPrice) { productsFilter = productsFilter.filter(item => { - const price = Number(item.price.replace(/€/g, '')) + const price = Number(item.price) if (price <= Number(params.maxPrice)) { return item } @@ -59,71 +48,77 @@ class ProductController { } if (params.maxPrice && params.minPrice) { productsFilter = productsFilter.filter(item => { - const price = Number(item.price.replace(/€/g, '')) + const price = Number(item.price) if (price >= Number(params.minPrice) && price <= Number(params.maxPrice)) { - console.log(price); - return item - } - }) - } - if (params.dateExpired) { - const [day, month, year] = params.dateExpired.split("/"); - const dateSearch = new Date(year, month - 1, day); - productsFilter = productsFilter.filter(item => { - const [day, month, year] = item.dateExpired.split("/"); - const dateProduct = new Date(year, month - 1, day); - if (dateProduct >= dateSearch) { return item } }) } + if (Number(params.bigPrice)) { productsFilter.sort((a, b) => { - const itemA = Number(a.price.replace(/€/g, '')) - const itemB = Number(b.price.replace(/€/g, '')) + const itemA = Number(a.price) + const itemB = Number(b.price) return itemB - itemA; }) } if (Number(params.lowPrice)) { productsFilter.sort((a, b) => { - const itemA = Number(a.price.replace(/€/g, '')) - const itemB = Number(b.price.replace(/€/g, '')) + const itemA = Number(a.price) + const itemB = Number(b.price) return itemA - itemB; }) } - if (params.isNew) { - productsFilter = productsFilter.filter(item => item.isNew === true) + if (Number(params.order_descending)) { + productsFilter.sort((a, b) => { + const itemA = a.order_position + const itemB = b.order_position + return itemB - itemA; + }) } - let productsFilterPages = [] - const totalItens = params?.itens ? Number(params.itens) : 200 - const page = params.page ? Number(params.page) : 1 - const startIndex = (totalItens * page) - totalItens - const lastIndex = (totalItens * page) - const products = productsFilter.slice(startIndex, lastIndex) - productsFilterPages.push({ - page: page, - productsPerPage: products.length, - products: products - }) + if (Number(params.order_crescent)) { + productsFilter.sort((a, b) => { + const itemA = a.order_position + const itemB = b.order_position + return itemA - itemB; + }) + } + + if (Number(params.isNew)) { + console.log(params.isNew); + productsFilter = productsFilter.filter(item => item.isNew == Number(params.isNew)) + } + + /* let productsFilterPages = [] + const totalItens = params?.itens ? Number(params.itens) : 200 + const page = params.page ? Number(params.page) : 1 + const startIndex = (totalItens * page) - totalItens + const lastIndex = (totalItens * page) + const products = productsFilter.slice(startIndex, lastIndex) + productsFilterPages.push({ + page: page, + productsPerPage: products.length, + products: products + }) */ return res.status(200).send({ - data: productsFilterPages + data: productsFilter }) } - findBySlug(req, res) { - const slug = req.params.slug - const products = productsJson - const filterSlug = products.filter(item => item.slug === slug) + async findById(req, res) { + const id = Number(req.params.id) + const _products = await db.getProducts(); + const filterProduct = _products[0].filter(item => item.id === id) return res.status(200).send({ - data: filterSlug + data: filterProduct }) } } -module.exports = new ProductController(); \ No newline at end of file +module.exports = new ProductController(); diff --git a/api/db/db.js b/api/db/db.js index b084634..5801e03 100644 --- a/api/db/db.js +++ b/api/db/db.js @@ -1,27 +1,27 @@ async function connect() { - if (global.connection && global.connection.state !== 'disconnected') - return global.connection; + if (global.connection && global.connection.state !== 'disconnected') + return global.connection; - const host = process.env.HOST; - const port = process.env.PORT; - const database = process.env.DATABASE; - const user = process.env.DB_USER; - const password = process.env.DB_PASSWORD; + const host = process.env.HOST; + const port = process.env.PORT; + const database = process.env.DATABASE; + const user = process.env.DB_USER; + const password = process.env.DB_PASSWORD; - const mysql = require("mysql2/promise"); - const connection = await mysql.createConnection("mysql://" + user + ":" + password + "@" + host + ":" + port + "/" + database + ""); - console.log("Connected to MySQL!"); - global.connection = connection; - return connection; + const mysql = require("mysql2/promise"); + const connection = await mysql.createConnection("mysql://" + user + ":" + password + "@" + host + ":" + port + "/" + database + ""); + console.log("Connected to MySQL!"); + global.connection = connection; + return connection; } -async function getProducts() { - const conn = await connect(); - const [rows] = await conn.query('CALL catalogue_get("2024-01-30", "08001")'); - - return rows; +async function getProducts(dateExpired, postalCode) { + console.log("Query in table MySQL!"); + const conn = await connect(); + const [rows] = await conn.query(`CALL catalogue_get("${dateExpired}", "${postalCode}")`); + return rows; } diff --git a/api/index.js b/api/index.js index 240daa2..40bcdd9 100644 --- a/api/index.js +++ b/api/index.js @@ -20,7 +20,7 @@ app.get('/', (req, res) => { //Products app.get('/api/products', productController.findAll); -app.get('/api/products/slug/:slug', productController.findBySlug); +app.get('/api/products/:id', productController.findById); app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); diff --git a/index.html b/index.html index a71fcf6..9329f89 100644 --- a/index.html +++ b/index.html @@ -51,7 +51,7 @@ src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-element-bundle.min.js" defer > - + diff --git a/quasar.config.js b/quasar.config.js index 65fdc62..7a6fab8 100644 --- a/quasar.config.js +++ b/quasar.config.js @@ -28,7 +28,7 @@ module.exports = configure(function (/* ctx */) { // app boot file (/src/boot) // --> boot files are part of "main.js" // https://v2.quasar.dev/quasar-cli-vite/boot-files - boot: ["i18n", "axios" /* , "stripe" */], + boot: ["i18n", "axios" /* , { path: "stripe", server: false } */], // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css css: ["app.scss"], @@ -119,7 +119,7 @@ module.exports = configure(function (/* ctx */) { // directives: [], // Quasar plugins - plugins: ["Meta", "Loading", "Notify"], + plugins: ["Meta", "Loading", "Notify", "LocalStorage", "SessionStorage"], }, // animations: 'all', // --- includes all animations diff --git a/src/boot/stripe.js b/src/boot/stripe.js index 4fd688b..6dabef9 100644 --- a/src/boot/stripe.js +++ b/src/boot/stripe.js @@ -3,15 +3,15 @@ import { boot } from "quasar/wrappers"; // "async" is optional; // more info on params: https://v2.quasar.dev/quasar-cli/boot-files -export default boot(async ({ app, router, store }) => { - if (typeof window === "undefined") return; - +export default boot(({ app }) => { const options = { - pk: process.env.STRIPE_PUBLISHABLE_KEY, - stripeAccount: process.env.STRIPE_ACCOUNT, - apiVersion: process.env.API_VERSION, - locale: process.env.LOCALE, + pk: "pk_test_51OZaJdIK1lTlG93d2y0B81n4XrjvjQwqfIUZ7ggb9wEBa1e4h34GlYFYPwjtGl3OUT7DJZlVNX9EMXaCdOBkIC3T007mLnfvCu", + stripeAccount: "acct_1OXQt7GMODwoSxWA", + apiVersion: "2023-10-16", + locale: "pt-BR", }; + app.config.globalProperties.$stripe = StripePlugin.install = options; + app.use(StripePlugin, options); }); diff --git a/src/components/@inputs/Calendar.vue b/src/components/@inputs/Calendar.vue index 241affe..a262502 100644 --- a/src/components/@inputs/Calendar.vue +++ b/src/components/@inputs/Calendar.vue @@ -1,63 +1,45 @@ + + diff --git a/src/components/ui/Card.vue b/src/components/ui/Card.vue index 6172fcf..4c9e992 100644 --- a/src/components/ui/Card.vue +++ b/src/components/ui/Card.vue @@ -46,22 +46,35 @@ export default defineComponent({ default: "", }, id: { - type: String, + type: Number, required: true, }, }, setup({ price, discount }) { const isLoaded = ref(false); + const isError = ref(false); const percent = +discount / 100; - //const priceWithoutLetter = ~~price.replaceAll("€", ""); + //const priceWithoutLetter = ~~price?.replaceAll("€", ""); const priceWithoutLetter = price; const finalValue = ~~(priceWithoutLetter - priceWithoutLetter * percent); + console.log(price); const onLoad = () => { isLoaded.value = true; }; - return { onLoad, isLoaded, finalValue, priceWithoutLetter }; + const onError = () => { + isError.value = true; + }; + + return { + onLoad, + onError, + isLoaded, + isError, + finalValue, + priceWithoutLetter, + }; }, }); @@ -82,10 +95,11 @@ export default defineComponent({

{{ finalValue }}€

-

+

{{ price }}€

diff --git a/src/components/ui/Modal.vue b/src/components/ui/Modal.vue index 7471cfd..11b40c9 100644 --- a/src/components/ui/Modal.vue +++ b/src/components/ui/Modal.vue @@ -30,6 +30,7 @@ export default defineComponent({ setup({ modalItem, typeModal }) { const { onSubmit, + setValues, fields: { calendar, calendarAttrs, @@ -61,6 +62,7 @@ export default defineComponent({ onSubmit, modalStore, modalTextContent, + setValues, postalCode, postalCodeAttrs, @@ -121,7 +123,7 @@ export default defineComponent({ v-if="modalItem === 'isOpenAvailability'" class="modal-body-availability" > - + Notify.create({ + message, + color: "info", + position: "top", + icon: "info", + timeout, + }), + default: () => { + console.error(`Type is invalid! TYPE: ${type}`) + } }; if (type) { - return obj[type]() || console.error(`Type is invalid! TYPE: ${type}`); + return obj[type]() || obj['default'](); } console.error("Type is required, success, warning or erro"); } diff --git a/src/hooks/useCheckoutForm.js b/src/hooks/useCheckoutForm.js new file mode 100644 index 0000000..2244fdb --- /dev/null +++ b/src/hooks/useCheckoutForm.js @@ -0,0 +1,192 @@ +import { toTypedSchema } from "@vee-validate/zod"; +import { useForm } from "vee-validate"; +import { computed, reactive, ref } from "vue"; + +import { useFormStore } from "src/stores/forms"; +import { checkoutSchema } from "src/utils/zod/schemas"; +import { useRouter } from "vue-router"; +import { useLocalStorage } from "./useLocalStorage"; + +export function useCheckoutForm() { + const { getItem } = useLocalStorage(); + const { push } = useRouter() + + const formStore = useFormStore(); + const { handleCheckoutData } = formStore; + const { meta, errors, handleSubmit, defineField, resetForm } = useForm({ + validationSchema: toTypedSchema(checkoutSchema), + initialValues: { + paymentMethod: "stripe", + terms: false, + }, + }); + + const [name, nameAttrs] = defineField("name"); + const [surname, surnameAttrs] = defineField("surname"); + const [address, addressAttrs] = defineField("address"); + const [postalCode, postalCodeAttrs] = defineField("postalCode"); + const [phone, phoneAttrs] = defineField("phone"); + const [city, cityAttrs] = defineField("city"); + const [province, provinceAttrs] = defineField("province"); + const [senderName, senderNameAttrs] = defineField("senderName"); + const [senderCifNif, senderCifNifAttrs] = defineField("senderCifNif"); + const [senderEmail, senderEmailAttrs] = defineField("senderEmail"); + const [senderPhone, senderPhoneAttrs] = defineField("senderPhone"); + const [senderNotes, senderNotesAttrs] = defineField("senderNotes"); + const [paymentMethod, paymentMethodAttrs] = defineField("paymentMethod"); + const [terms, termsAttrs] = defineField("terms"); + + const provinceOptions = ref([ + { code: "01", name: "Araba/Álava" }, + { code: "02", name: "Albacete" }, + { code: "03", name: "Alicante/Alacant" }, + { code: "04", name: "Almería" }, + { code: "05", name: "Ávila" }, + { code: "06", name: "Badajoz" }, + { code: "07", name: "Balears, Illes" }, + { code: "08", name: "Barcelona" }, + { code: "09", name: "Burgos" }, + { code: "10", name: "Cáceres" }, + { code: "11", name: "Cádiz" }, + { code: "12", name: "Castellón/Castelló" }, + { code: "13", name: "Ciudad Real" }, + { code: "14", name: "Córdoba" }, + { code: "15", name: "Coruña, A" }, + { code: "16", name: "Cuenca" }, + { code: "17", name: "Girona" }, + { code: "18", name: "Granada" }, + { code: "19", name: "Guadalajara" }, + { code: "20", name: "Gipuzkoa" }, + { code: "21", name: "Huelva" }, + { code: "22", name: "Huesca" }, + { code: "23", name: "Jaén" }, + { code: "24", name: "León" }, + { code: "25", name: "Lleida" }, + { code: "26", name: "Rioja, La" }, + { code: "27", name: "Lugo" }, + { code: "28", name: "Madrid" }, + { code: "29", name: "Málaga" }, + { code: "30", name: "Murcia" }, + { code: "31", name: "Navarra" }, + { code: "32", name: "Ourense" }, + { code: "33", name: "Asturias" }, + { code: "34", name: "Palencia" }, + { code: "35", name: "Palmas, Las" }, + { code: "36", name: "Pontevedra" }, + { code: "37", name: "Salamanca" }, + { code: "38", name: "Santa Cruz de Tenerife" }, + { code: "39", name: "Cantabria" }, + { code: "40", name: "Segovia" }, + { code: "41", name: "Sevilla" }, + { code: "42", name: "Soria" }, + { code: "43", name: "Tarragona" }, + { code: "44", name: "Teruel" }, + { code: "45", name: "Toledo" }, + { code: "46", name: "Valencia/València" }, + { code: "47", name: "Valladolid" }, + { code: "48", name: "Bizkaia" }, + { code: "49", name: "Zamora" }, + { code: "50", name: "Zaragoza" }, + { code: "51", name: "Ceuta" }, + { code: "52", name: "Melilla" }, + ]); + + const stepActive = reactive({ data: 1 }); + const stepList = reactive({ + data: [ + { + value: 1, + name: "Paso 1", + description: "Datos de facturación", + active: true, + }, + { + value: 2, + name: "Paso 2", + description: "Confirmación", + active: false, + }, + { + value: 3, + name: "Paso 3", + description: "Pago", + active: false, + }, + ], + }); + const stepsFormated = computed(() => { + return stepList["data"].map((step) => { + if (step.value === stepActive["data"]) { + return { ...step, active: true }; + } + return step; + }); + }); + + const handleClickStep = (value) => { + stepActive["data"] = value; + }; + + const checkoutBlock = ref(true); + const onSubmit = handleSubmit((values) => { + handleCheckoutData(values); + stepList.data[2].active = true; + checkoutBlock.value = false; + resetForm(); + }); + + const cart = getItem("cart"); + const totalPrice = ref(0) + totalPrice.value = cart?.reduce((acc, { price }) => { + if (price) { + const priceWithoutLetter = price?.replace("€", ""); + return +priceWithoutLetter + acc; + } + }, 0); + + return { + handleClickStep, + provinceOptions, + stepsFormated, + stepList, + checkoutBlock, + cart, + totalPrice, + formState: { + meta, + errors, + onSubmit, + submitLoading: ref(false), + }, + fields: { + name, + nameAttrs, + surname, + surnameAttrs, + address, + addressAttrs, + postalCode, + postalCodeAttrs, + phone, + phoneAttrs, + city, + cityAttrs, + province, + provinceAttrs, + senderName, + senderNameAttrs, + senderCifNif, + senderCifNifAttrs, + senderEmail, + senderEmailAttrs, + senderPhone, + senderPhoneAttrs, + senderNotes, + senderNotesAttrs, + paymentMethod, + paymentMethodAttrs, + terms, + termsAttrs, + }, + }; +} diff --git a/src/hooks/useLocalStorage.js b/src/hooks/useLocalStorage.js new file mode 100644 index 0000000..68a4fdf --- /dev/null +++ b/src/hooks/useLocalStorage.js @@ -0,0 +1,25 @@ +import { LocalStorage } from "quasar"; + +export function useLocalStorage() { + const addItem = (key, value) => { + LocalStorage.set(`@${key}`, value); + }; + + const getItem = (key) => { + const data = JSON.parse(LocalStorage.getItem(`@${key}`)); + + + return (data || []); + }; + + const removeItem = (key) => { + LocalStorage.remove(`@${key}`); + }; + + + return { + addItem, + getItem, + removeItem, + }; +} diff --git a/src/hooks/usePostalCalendar.js b/src/hooks/usePostalCalendar.js index e51fffe..b498b4e 100644 --- a/src/hooks/usePostalCalendar.js +++ b/src/hooks/usePostalCalendar.js @@ -1,10 +1,10 @@ import { toTypedSchema } from "@vee-validate/zod"; import { storeToRefs } from "pinia"; import { useForm } from "vee-validate"; -import { watch } from "vue"; -import { useRouter } from "vue-router"; +import { ref, watch } from "vue"; +import { useRoute, useRouter } from "vue-router"; -import { apiBack } from "src/boot/axios"; +import { invertDate } from "src/functions/invertDate"; import { quasarNotify } from "src/functions/quasarNotify"; import { useCartStore } from "src/stores/cart"; import { useFormStore } from "src/stores/forms"; @@ -13,38 +13,50 @@ import { useRangePriceStore } from "src/stores/rangePrice"; import { availabilitySchema } from "src/utils/zod/schemas"; import { rangePriceSchema } from "src/utils/zod/schemas/rangePriceSchema"; +/** + * Custom hook for managing the postal and calendar functionality. + * + * @param {Object} options - The options for the hook. + * @param {string} options.modalItem - The modal item isOpenAvailability || isOpenFilters. + * @param {string} options.type - The type of the hook. home || product || availability || filter + * @returns {Object} - The hook functions and data. + */ export function usePostalCalendar({ modalItem = "", type = "home" }) { - const { push } = useRouter(); + const route = useRoute(); + const { push } = useRouter() const rangePriceStore = useRangePriceStore(); const { rangeValue } = storeToRefs(rangePriceStore); const modalStore = useModalStore(); - const { isOpenAvailability } = storeToRefs(modalStore); + const { openModal } = modalStore const formStore = useFormStore(); - const { availability, sortProductFilters } = storeToRefs(formStore); + const { sortProductFilters } = storeToRefs(formStore); const cartStore = useCartStore(); const { addToCart, getProducts } = cartStore; - const { currentProduct, products } = storeToRefs(cartStore); + const { products, homeSection } = storeToRefs(cartStore); const min = 0; const max = 200; + const category = ref(route.path.split("/")[2]) - const { handleSubmit, handleReset, defineField, errors } = useForm({ - validationSchema: toTypedSchema( - type !== "filter" ? availabilitySchema : rangePriceSchema - ), - initialValues: { - range: { - min, - max, + const { handleSubmit, handleReset, defineField, errors, setValues } = useForm( + { + validationSchema: toTypedSchema( + type !== "filter" ? availabilitySchema : rangePriceSchema + ), + initialValues: { + range: { + min, + max, + }, + postalCode: "", + date: "", }, - postalCode: "", - date: "", - }, - }); + } + ); const [calendar, calendarAttrs] = defineField("date"); const [postalCode, postalCodeAttrs] = defineField("postalCode"); const [priceRange, priceRangeAttrs] = defineField("range"); @@ -56,6 +68,8 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) { quasarNotify({ message: newErrors.postalCode, type: "erro" }), date: () => quasarNotify({ message: newErrors.date, type: "erro" }), range: () => quasarNotify({ message: newErrors.range, type: "erro" }), + dedication: () => + quasarNotify({ message: newErrors.dedication, type: "erro" }), }; const keys = Object.keys(newErrors); keys.forEach((key) => { @@ -63,47 +77,73 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) { }); }); + watch( + [() => route.path, () => sortProductFilters.value], + ([newPath]) => { + const categoryPath = newPath.split("/")[2]; + category.value = categoryPath; + } + ); + const onSubmit = handleSubmit((values) => { const postalAndDateParams = { postalCode: values.postalCode, - dateExpired: values.date, + dateExpired: invertDate(values.date), + }; + + const categoryObj = { + plantas: "Floranet Plantas", + ramos: "Floranet Ramos", }; const objVal = { home: async () => { console.log(type); - getProducts( + await getProducts( { - itens: 30, - category: 1, postalCode: values.postalCode, - dateExpired: values.date, + dateExpired: invertDate(values.date), }, - () => push("/categoria/ramos") + () => homeSection.value.scrollIntoView() ); }, product: async () => { console.log(type); - try { - const { - data: { data }, - } = await apiBack.get(`products/slug/${currentProduct.value.id}`, { - params: postalAndDateParams, - }); - } catch (error) { - console.error(error); - push("/"); + await getProducts(postalAndDateParams); + + const hasProduct = products.value.data.some((item) => { + const date = new Date(item.dateExpired); + const day = date.getDate(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const year = date.getFullYear(); + const dateExpired = `${day}/${month}/${year}`; + + const id = +route.path.split('/')[2]; + + return item.postalCode === values.postalCode && item.id === id && values.date <= dateExpired + }); + + if (!hasProduct) { + push('/categoria/ramos') + quasarNotify({ message: 'Seleccione una nueva fecha y un nuevo código postal.', type: 'warning' }) + + setTimeout(() => { + openModal({ modal: 'availability' }) + }, 2000) + + return } + + addToCart(products.value.current, dedication) }, availability: async () => { console.log(type); - getProducts({ - itens: 20, + await getProducts({ postalCode: values.postalCode, - dateExpired: values.date, + dateExpired: invertDate(values.date), }); }, filter: async () => { @@ -112,26 +152,21 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) { rangeValue.value.max = values.range.max; rangeValue.value.min = values.range.min; - const category = sortProductFilters.value.category; - const categoryObj = { - plantas: 1, - ramos: 2, - }; - const params = { - itens: 20, - category: categoryObj[category], + type: categoryObj[category.value], minPrice: values.range.min, maxPrice: values.range.max, }; - getProducts(params); + await getProducts(params); + }, + default: () => { + console.error( + `INVALID TYPE! TYPE: ${type}, ONLY HOME, PRODUCT AND FILTER ARE VALID!` + ); }, }; - objVal[type]() || - console.error( - `INVALID TYPE! TYPE: ${type}, ONLY HOME, PRODUCT AND FILTER ARE VALID!` - ); + objVal[type]() || objVal["default"](); if (modalItem) { modalStore[modalItem] = false; @@ -141,6 +176,7 @@ export function usePostalCalendar({ modalItem = "", type = "home" }) { return { onSubmit, + setValues, modalStore, fields: { calendar, diff --git a/src/hooks/useProductPage.js b/src/hooks/useProductPage.js new file mode 100644 index 0000000..e720b35 --- /dev/null +++ b/src/hooks/useProductPage.js @@ -0,0 +1,51 @@ +import { storeToRefs } from "pinia"; +import { useCartStore } from "src/stores/cart"; +import { useModalStore } from "src/stores/modalStore"; +import { watch } from "vue"; +import { useRoute } from "vue-router"; +import { usePostalCalendar } from "./usePostalCalendar"; + +export function useProductPage() { + const route = useRoute(); + + const { + fields: { dedication, dedicationAttrs }, + } = usePostalCalendar({ modalItem: "isOpenAvailability" }); + + const modalStore = useModalStore(); + const { openModal } = modalStore; + + const cartStore = useCartStore(); + const { getProduct, getProducts } = cartStore; + const { products, featuredProducts, addCartLoadingBtn } = + storeToRefs(cartStore); + + watch( + () => products.value.current?.type, + (newCategory) => { + getProducts({ + // type: newCategory, + }); + } + ); + + watch( + () => route.params.id, + (newId) => { + getProduct(newId); + } + ); + + const checkImageValidity = (imageLink) => { + const validExtensions = [".jpg", ".jpeg", ".png"]; + + if (imageLink) { + const extension = imageLink.substring(imageLink.lastIndexOf(".")); + return validExtensions.includes(extension.toLowerCase()); + } + + return true; + }; + + return { checkImageValidity, openModal } +} diff --git a/src/layouts/DefaultLayout.vue b/src/layouts/DefaultLayout.vue index 7d31a69..8b2376d 100644 --- a/src/layouts/DefaultLayout.vue +++ b/src/layouts/DefaultLayout.vue @@ -27,9 +27,11 @@ export default defineComponent({ - - - + + + + + diff --git a/src/mock/cards.js b/src/mock/cards.js index 62776e2..e64c41b 100644 --- a/src/mock/cards.js +++ b/src/mock/cards.js @@ -26,7 +26,7 @@ export function mockGenerator({ length }) { slug: fakerES.commerce.isbn({ separator: "-", variant: 13 }), category: fakerES.number.int({ min: 1, max: 2 }), postalCode: "12345", - dateExpired: "30/01/2024", + dateExpired: "2024-01-30", images: Array.from( { length: fakerES.number.int({ min: 2, max: 6 }) }, () => fakerES.image.urlPicsumPhotos() diff --git a/src/pages/CategoryPage.vue b/src/pages/CategoryPage.vue index ef6167d..5efc813 100644 --- a/src/pages/CategoryPage.vue +++ b/src/pages/CategoryPage.vue @@ -12,8 +12,10 @@ import DudasSection from "src/components/sections/DudasSection.vue"; import Card from "src/components/ui/Card.vue"; import Container from "src/components/ui/Container.vue"; import Modal from "src/components/ui/Modal.vue"; + import { useCartStore } from "src/stores/cart"; import { useFormStore } from "src/stores/forms"; +import { useMobileStore } from "src/stores/mobileNav"; import { useModalStore } from "src/stores/modalStore"; export default defineComponent({ @@ -32,6 +34,9 @@ export default defineComponent({ setup() { const route = useRoute(); + const mobileStore = useMobileStore(); + const { screenWidth } = storeToRefs(mobileStore); + const modalStore = useModalStore(); const { openModal } = modalStore; @@ -66,8 +71,8 @@ export default defineComponent({ latest: "más recientes", }; const categoryObj = { - plantas: 1, - ramos: 2, + plantas: "Floranet Plantas", + ramos: "Floranet Ramos", }; watch(availability, (newDate) => { @@ -83,14 +88,13 @@ export default defineComponent({ sortProductFilters.value.category = categoryPath; const params = { - category: categoryObj[categoryPath], - itens: window.screen.width <= 445 ? 16 : 20, + type: categoryObj[categoryPath], }; const paramsObj = { "lowest-price": () => (params.lowPrice = 1), "highest-price": () => (params.bigPrice = 1), latest: () => (params.isNew = 1), - // recommended: () => params.featured = 1, + recommended: () => (params.recommend = 1), }; if (newOrder) { paramsObj[newOrder](); @@ -104,8 +108,7 @@ export default defineComponent({ const categoryPath = route.path.split("/")[2]; await getProducts({ - category: categoryObj[categoryPath], - itens: window.screen.width <= 445 ? 16 : 20, + type: categoryObj[categoryPath], }); }); @@ -122,11 +125,12 @@ export default defineComponent({ } return { - sortProductFilters, openOrderFilter, openModal, + sortProductFilters, availability, isOpenOrder, + screenWidth, modalStore, orderText, products, @@ -145,9 +149,6 @@ export default defineComponent({ {{ sortProductFilters.category }} para obsequiar

- Descripción SEO: Lorem ipsum dolor sit amet, consectetur - adipiscing elit, sed do eiusmod tempor incididunt ut labore et - dolore magna aliqua.

@@ -218,25 +219,14 @@ export default defineComponent({
-