From ee118a5b8523d1a452f6681cf5bac55ffc76bccd Mon Sep 17 00:00:00 2001 From: Jesus Fajardo Date: Thu, 12 Sep 2024 09:14:10 +0200 Subject: [PATCH] Adding table with purchase courses --- backend/controller/PurchaseController.js | 143 +++++++----------- backend/service/PurchaseService.js | 84 ++++++---- .../src/components/table/PurchasesTable.jsx | 59 ++++++-- frontend/src/hooks/useGetPurchases.js | 44 ++++-- 4 files changed, 178 insertions(+), 152 deletions(-) diff --git a/backend/controller/PurchaseController.js b/backend/controller/PurchaseController.js index 1f6e322..f4a6654 100644 --- a/backend/controller/PurchaseController.js +++ b/backend/controller/PurchaseController.js @@ -31,9 +31,35 @@ const PurchaseService = require("../service/PurchaseService"); * /api/purchases/: * get: * summary: Get list of purchases - * description: Retrieve a list of all purchases with details such as email, birth date, purchase date, meet date, and price. - * tags: - * - Purchases + * description: Retrieve a list of all purchases with optional filters by date. + * tags: [Purchases] + * parameters: + * - in: query + * name: page + * schema: + * type: int32 + * required: false + * description: Page number + * - in: query + * name: limit + * schema: + * type: int32 + * required: false + * description: Limit + * - in: query + * name: startDate + * schema: + * type: string + * format: date + * required: false + * description: Filter purchases from this start date (inclusive). + * - in: query + * name: endDate + * schema: + * type: string + * format: date + * required: false + * description: Filter purchases up to this end date (inclusive). * responses: * '200': * description: A list of purchases @@ -114,101 +140,34 @@ router.post('/', async (req, res) => { const savedPurchase = await PurchaseService.save(purchase); res.json(savedPurchase); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } }); router.get('/', async (req, res) => { try { - const purchases = [ - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'test@test.com', - birthDate: '01/01/2000', - purchaseDate: '01/01/2021', - meetDate: '01/01/2021', - price: 15 - }, - { - email: 'jane@test.com', - birthDate: '05/05/1998', - purchaseDate: '02/02/2022', - meetDate: '01/01/2021', - price: 15 - } - ] - res.json(purchases); + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 10; + const startDate = req.query.startDate ? new Date(req.query.startDate) : null; + const endDate = req.query.endDate ? new Date(req.query.endDate) : null; + + const { totalItems, totalPages, currentPage, itemsPerPage, purchases } = await PurchaseService.getPaginatedPurchases(page, limit, startDate, endDate); + + const paginationInfo = { + currentPage, + itemsPerPage, + totalItems, + totalPages, + hasNextPage: currentPage < totalPages, + hasPreviousPage: currentPage > 1 + }; + + res.json({ + paginationInfo, + data: purchases + }); } catch (error) { - res.status(500).json({error: error.message}); + res.status(500).json({ error: error.message }); } }); diff --git a/backend/service/PurchaseService.js b/backend/service/PurchaseService.js index 1423c6f..1f2bdaa 100644 --- a/backend/service/PurchaseService.js +++ b/backend/service/PurchaseService.js @@ -1,39 +1,67 @@ -const format = require('date-fns/format'); -const purchaseRepository = require('../persistance/PurchaseRepository') -const meetRepository = require('../persistance/MeetRepository') +const purchaseRepository = require('../persistance/PurchaseRepository'); +const meetRepository = require('../persistance/MeetRepository'); const userRepository = require("../persistance/UserRepository"); -const mailerService = require('./MailerService') -const invoiceMakerService = require('./InvoiceMakerService') -const PurchaseService = {} +const {Op, Sequelize} = require('sequelize'); +const PurchaseService = {}; PurchaseService.save = async function (purchase) { - const meet = await meetRepository.findByPk(purchase.meetId) - const user = await userRepository.findByPk(purchase.userId) - const newPurchase = await purchaseRepository.create({ + const meet = await meetRepository.findByPk(purchase.meetId); + const user = await userRepository.findByPk(purchase.userId); + return await purchaseRepository.create({ userId: user.id, meetId: meet.id, price: meet.price, - purchaseDate: new Date() - }) + purchaseDate: new Date(), + }); +}; - const dataForPdf = { - id: newPurchase.id, - purchaseDate: format(newPurchase.purchaseDate, 'dd/MM/yyyy HH:mm'), - price: meet.price, - items: [ - {product: "Clase de Karate", quantity: 1, cost: meet.price} - ] +PurchaseService.getPaginatedPurchases = async function (page = 1, limit = 10, startDate = null, endDate = null) { + const offset = (page - 1) * limit; + + // Crear el objeto de filtrado por fecha si se proporcionan fechas válidas + const whereConditions = {}; + + if (startDate) { + const startOfDay = new Date(startDate); + startOfDay.setHours(0, 0, 0, 0); // 00:00:00.000 + + // Aplicar el filtro sobre la tabla Meet + whereConditions['$Meet.meetDate$'] = {[Op.gte]: startOfDay}; } - const invoicePdf = await invoiceMakerService.generateInvoicePdf(dataForPdf) - await mailerService.sendMail( - user.email, - 'Purchase confirmation', - 'Your purchase has been processed', - 'Your purchase has been processed', - invoicePdf) + if (endDate) { + const endOfDay = new Date(endDate); + endOfDay.setHours(23, 59, 59, 999); // 23:59:59.999 + + // Aplicar el filtro sobre la tabla Meet + whereConditions['$Meet.meetDate$'] = { + ...whereConditions['$Meet.meetDate$'], + [Op.lte]: endOfDay + }; + } + + // Consultar las compras con los filtros de fecha sobre Meet.meetDate, paginación y contar el total de registros + const {count, rows: purchases} = await purchaseRepository.findAndCountAll({ + where: whereConditions, // Filtrar por las fechas en la tabla Meet + include: [ + {model: userRepository, attributes: ['id', 'email']}, + {model: meetRepository, attributes: ['id', 'meetDate']} + ], + offset: offset, + limit: limit, + order: [['purchaseDate', 'DESC']], // Ordenar por fecha de compra descendente + }); + + // Calcular la información de paginación + const totalPages = Math.ceil(count / limit); - return newPurchase -} + return { + totalItems: count, + totalPages: totalPages, + currentPage: page, + itemsPerPage: limit, + purchases: purchases, + }; +}; -module.exports = PurchaseService \ No newline at end of file +module.exports = PurchaseService; diff --git a/frontend/src/components/table/PurchasesTable.jsx b/frontend/src/components/table/PurchasesTable.jsx index e9a7509..b17db04 100644 --- a/frontend/src/components/table/PurchasesTable.jsx +++ b/frontend/src/components/table/PurchasesTable.jsx @@ -2,27 +2,48 @@ import {useState} from "react"; import {DataTable} from 'primereact/datatable'; import {Column} from 'primereact/column'; import {Calendar} from "primereact/calendar"; -import 'primereact/resources/themes/saga-blue/theme.css'; // Elige el tema que prefieras +import 'primereact/resources/themes/saga-blue/theme.css'; import 'primereact/resources/primereact.min.css'; import 'primeicons/primeicons.css'; -import {Button} from "primereact/button"; import useGetPurchases from "../../hooks/useGetPurchases"; import {hasSession, isUser} from "../../utils/session"; +import {format} from 'date-fns'; export default function PurchasesTable() { const userSession = hasSession(); const isUserValue = isUser(); - const {purchases, error} = useGetPurchases(isUserValue, userSession); - const [selectedStartDate, setSelectedStartDate] = useState(new Date()); - const [selectedEndDate, setSelectedEndDate] = useState(new Date()); - function searchMeet() { - console.log('Meet saved'); - } + const [page, setPage] = useState(1); + const [rowsPerPage, setRowsPerPage] = useState(4); + + const [selectedStartDate, setSelectedStartDate] = useState(null); // Se inicia como null para no filtrar inicialmente + const [selectedEndDate, setSelectedEndDate] = useState(null); + + const {purchases, totalRecords, error} = useGetPurchases(isUserValue, userSession, page, rowsPerPage, selectedStartDate, selectedEndDate); + + const onPageChange = (event) => { + setPage(event.page + 1); // PrimeReact usa índices basados en 0 + setRowsPerPage(event.rows); // Actualizar el número de filas por página + }; + + const formatDate = (dateString) => { + return format(new Date(dateString), 'dd/MM/yyyy'); + }; + + const purchaseDateTemplate = (rowData) => { + return formatDate(rowData.purchaseDate); + }; + + const meetDateTemplate = (rowData) => { + return formatDate(rowData.Meet.meetDate); + }; return (
+
+ +
setSelectedStartDate(e.value)} dateFormat="dd/mm/yy" placeholder="Inicio"/> @@ -31,15 +52,21 @@ export default function PurchasesTable() { setSelectedEndDate(e.value)} dateFormat="dd/mm/yy" placeholder="Fin"/>
-
-
-
- - - - +
+ {error &&
Error: {error}
} {/* Mostrar error si existe */} + + + +
diff --git a/frontend/src/hooks/useGetPurchases.js b/frontend/src/hooks/useGetPurchases.js index 0d87939..fa3f664 100644 --- a/frontend/src/hooks/useGetPurchases.js +++ b/frontend/src/hooks/useGetPurchases.js @@ -1,44 +1,56 @@ import {useEffect, useState} from 'react'; import {getApplicationDomain, getBase64CredentialsFromSession} from "../utils/session"; -function useGetPurchases(isUser, hasSession) { +function useGetPurchases(isUser, hasSession, page = 1, limit = 10, startDate, endDate) { const [purchases, setPurchases] = useState([]); + const [totalRecords, setTotalRecords] = useState(0); // Total de registros para la paginación const [error, setError] = useState(null); const base64Credentials = getBase64CredentialsFromSession() useEffect(() => { async function getPurchases() { try { - const domain = getApplicationDomain() - const response = await fetch(domain + '/purchases', { + const domain = getApplicationDomain(); + let url = `${domain}/purchases?page=${page}&limit=${limit}`; + + // Añadir los parámetros de filtrado por fecha si están seleccionados + if (startDate) { + url += `&startDate=${startDate.toISOString()}`; // Enviar la fecha en formato ISO + } + if (endDate) { + url += `&endDate=${endDate.toISOString()}`; + } + + const response = await fetch(url, { headers: { 'Authorization': `Basic ${base64Credentials}` } }); if (!response.ok) { - console.error('Network response was not ok', error); - setError(error); + const errorMessage = `Network response was not ok: ${response.statusText}`; + console.error(errorMessage); + setError(errorMessage); + return; } - const purchaseList = await response.json(); - - console.log("Lista -> " + purchaseList) + const { paginationInfo, data } = await response.json(); - setPurchases(purchaseList || []); + setPurchases(data || []); + setTotalRecords(paginationInfo.totalItems); // Guarda el total de registros para la paginación } catch (error) { - console.error('Error fetching meets:', error); - setError(error); + console.error('Error fetching purchases:', error); + setError(error.message); } - setPurchases(purchases) } - if (hasSession && isUser) { - getPurchases() + // Llamar al servicio solo si el usuario tiene sesión y es válido + if (hasSession) { + getPurchases(); } - }, [isUser, hasSession, purchases, base64Credentials, error]); + }, [isUser, hasSession, page, limit, startDate, endDate, base64Credentials]); - return {purchases, error}; + return {purchases, totalRecords, error}; } export default useGetPurchases;