diff --git a/front/components/comments/CreateComments.jsx b/front/components/comments/CreateComments.jsx index 1e693e1a..640fcc13 100644 --- a/front/components/comments/CreateComments.jsx +++ b/front/components/comments/CreateComments.jsx @@ -7,7 +7,7 @@ import { createComment } from "../../redux/actions/actionsComment"; import Swal from "sweetalert2"; import { getSesion } from "../../redux/actions/actionsUser"; import { getCommentsToDetail } from "../../redux/actions/actions"; -getCommentsToDetail + const CreateComments = ({ project, replyTo }) => { const router = useRouter(); diff --git a/front/components/feed/IndexFeed.jsx b/front/components/feed/IndexFeed.jsx index 39508f3f..754a3aff 100644 --- a/front/components/feed/IndexFeed.jsx +++ b/front/components/feed/IndexFeed.jsx @@ -17,13 +17,13 @@ const IndexFeed = () => { - + {/* - + */} //
diff --git a/front/components/feed/Menu.jsx b/front/components/feed/Menu.jsx index 8176fab1..ea9295e3 100644 --- a/front/components/feed/Menu.jsx +++ b/front/components/feed/Menu.jsx @@ -63,7 +63,7 @@ const Menu = () => { Posts - + {/* Rating @@ -76,7 +76,7 @@ const Menu = () => { > Mi Actividad - + */} ); diff --git a/front/components/socialMedia.jsx b/front/components/socialMedia.jsx index 110e01cc..3f1cab34 100644 --- a/front/components/socialMedia.jsx +++ b/front/components/socialMedia.jsx @@ -1,36 +1,36 @@ +import { Link, link } from "@nextui-org/react"; import { FaTwitter, FaEnvelope, FaLinkedin, FaGithub } from "react-icons/fa"; -const SocialMedia = ({email}) => { + +const SocialMedia = ({ user }) => { + const { email, githubUser, linkedinUser, twitterUser } = user; return (
- - + - + - + - + - + - + - +
); }; diff --git a/front/components/userDashboard/Buttons.jsx b/front/components/userDashboard/Buttons.jsx new file mode 100644 index 00000000..b155bde4 --- /dev/null +++ b/front/components/userDashboard/Buttons.jsx @@ -0,0 +1,63 @@ +import { useDispatch, useSelector } from "react-redux"; +import { useEffect, useState } from "react"; + +import { restoreProjects } from "../../redux/actions/actions"; +import { deleteProjects } from "../../redux/actions/actions"; + +import {Button} from "@nextui-org/react"; +import Swal from "sweetalert2"; + +const Buttons = ({ id }) => { + + + const dispatch = useDispatch() + const [activeButton, setActiveButton] = useState(false); + const [desactiveButton, setDesactiveButton] = useState(true); + + const active = () => { + dispatch(restoreProjects(id)); + Swal.fire({ + icon: "success", + title: "Proyecto activado con exito!", + showConfirmButton: true, + timer: 3500, + }); + + setActiveButton(false); + setDesactiveButton(true); + }; + const desactive = () => { + dispatch(deleteProjects(id)) + Swal.fire({ + icon: "success", + title: "Proyecto desactivado con exito!", + showConfirmButton: true, + timer: 3500, + }) + setActiveButton(true); + setDesactiveButton(false); + } + + + + return ( +
+ + +
+ ); +}; + +export default Buttons; diff --git a/front/components/userDashboard/OrdenesCompra.jsx b/front/components/userDashboard/OrdenesCompra.jsx index a7f0bca6..053ffd1f 100644 --- a/front/components/userDashboard/OrdenesCompra.jsx +++ b/front/components/userDashboard/OrdenesCompra.jsx @@ -1,3 +1,5 @@ +import { useState, useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { Table, TableHeader, @@ -8,112 +10,201 @@ import { getKeyValue, Select, SelectItem, - Input + Input, + Link, + Button } from "@nextui-org/react"; +// import { PDFDownload } from "./PDFDownload"; +import { getOrder } from "../../redux/actions/actionsPayments"; +import { jsPDF } from "jspdf"; +import "jspdf-autotable"; +import OrderDetail from "./OrderDetail"; + + +const OrdenesCompra = ({name, projects}) => { + +console.log(name); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(getOrder()); + }, [dispatch]); + + const order = useSelector((state) => state.paymentData.payments); + + const orderByuser = order.filter((o) => o.buyer.toUpperCase() === name.toUpperCase()) + + const ordersShort = orderByuser.slice(0, 5) + console.log(ordersShort); +//buscar por orden de compra +// const [compra, setCompra] = useState(""); +// console.log(compra); +// const searchBuy = ordersShort.filter((r) => Number(r.id) === Number(compra)); +// console.log(searchBuy); + //filtrar por estado de la compra + // const [filter, setFilter] = useState([]) + // const filterOrders = () => { + + // } +// const PDFDownload = () => { +// // Crear un nuevo documento PDF +// const doc = new jsPDF("l"); +// doc.setFont("helvetica"); +// doc.setFontSize(30); +// doc.text("Orden de compra", 145, 20, null, null, "center"); +// // const imgData = image; +// // doc.addImage(imgData, "PNG", 15, 40, 148, 210); +// // Agregar contenido al PDF + +// // doc.text("Orden de compra", 105, 15, 15, null, "center"); +// // doc.addFont("/fonts/Pompiere-Regular.ttf", "Pompiere", "regular"); +// // const data = ordersShort.map((o) => { +// // o.name, o.status, o.price +// // }) +// doc.autoTable({ +// startY: 28, +// theme: "striped", +// styles: { +// fontSize: 5, +// overflow: "linebreak", +// cellPadding: 2, +// lineColor: [0, 0, 0], +// lineWidth: 0.2, +// }, +// headStyles: { +// valign: "middle", +// halign: "center", +// fontSize: 15, +// fillColor: [255, 255, 255], +// textColor: [0, 0, 0], +// }, +// tableLineColor: [0, 0, 0], +// tableLineWidth: 0.5, +// columnStyles: { +// 0: { +// halign: "center", +// }, +// 1: { +// halign: "center", +// }, +// 2: { +// halign: "center", +// }, +// 3: { +// halign: "center", +// }, +// 4: { +// halign: "center", +// }, +// 5: { +// halign: "left", +// }, +// }, +// bodyStyles: { +// fillColor: [255, 255, 255], +// textColor: 0, +// fontSize: 10, +// minCellHeight: 15, +// }, +// head: [ +// ["Fecha", "N° Compra", "Estado", "Producto", "Precio", "desarrollador"], +// ], +// // body: [["17/06/2023", "AE345TG", "CANCELADA", "LARAVEL", "$45", "Steve "] +// // body: data, + + +// }) + +// // Descargar el PDF +// doc.save("OrdenDeCompra.pdf"); +// }; -const columns = [ - { - key: "compra", - label: "N° COMPRA", - }, - { - key: "date", - label: "FECHA", - }, - { - key: "status", - label: "ESTADO", - }, - { - key: "product", - label: "PRODUCTO", - }, - { - key: "price", - label: "PRECIO", - }, -]; -const rows = [ - { - key: "1", - compra: "2AWERF45", - date: "04/06/2023", - status: "Facturada", - product: "Laravel", - price: "34", - }, - { - key: "2", - compra: "2AWERF45", - date: "04/06/2023", - status: "Facturada", - product: "Laravel", - price: "34", - }, - { - key: "3", - compra: "2AWERF45", - date: "04/06/2023", - status: "Facturada", - product: "Laravel", - price: "34", - }, - { - key: "4", - compra: "2AWERF45", - date: "04/06/2023", - status: "Cancelada", - product: "Laravel", - price: "34", - }, -]; - -const OrdenesCompra = () => { return ( -
+
- - Todos + Facturada + Cancelada + */} + {/* - - + */}
- {(column) => ( + {/* {(column) => ( {column.label} - )} + )} */} + FECHA + N° COMPRA + ESTADO + PRODUCTO + PRECIO + DETALLES + DESCARGA - - {(item) => ( + + {/* {(item) => ( {(columnKey) => ( {getKeyValue(item, columnKey)} )} - )} + )} */} + + {ordersShort.map((order) => ( + + {order?.createdAt ?order.createdAt.slice(0,10) :

Se desconoce la fecha

}
+ {order?.id} + {order?.status.toUpperCase()} + {order?.product} + ${order?.paymentAmount} + + + {/* y */} + {/* */} + + {/* */} + + + + +
+ ))}
diff --git a/front/components/userDashboard/OrderDetail.js b/front/components/userDashboard/OrderDetail.js new file mode 100644 index 00000000..7044c2de --- /dev/null +++ b/front/components/userDashboard/OrderDetail.js @@ -0,0 +1,113 @@ +import { useState } from "react"; +import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure} from "@nextui-org/react"; + +// const order = [ +// { +// key: 1, +// compra: "2AWERF45", +// date: "04/06/2023", +// status: "Facturada", +// product: "Laravel", +// price: "34", +// description: "hoola mundo", +// desarrollador: "steve jobs", +// }, +// { +// key: 2, +// compra: "2AWERF46", +// date: "04/06/2023", +// status: "Facturada", +// product: "Laravel", +// price: "34", +// description: "hoola mundo", +// desarrollador: "steve jobs", +// }, +// { +// key: 3, +// compra: "2AWERF47", +// date: "04/06/2023", +// status: "Facturada", +// product: "Laravel", +// price: "34", +// description: "hoola mundo", +// desarrollador: "steve jobs", +// }, +// { +// key: 4, +// compra: "2AWERF48", +// date: "04/06/2023", +// status: "Cancelada", +// product: "Laravel", +// price: "34", +// description: "hoola mundo", +// desarrollador: "steve jobs", +// }, +// ]; +const OrderDetail = ({id, order}) => { + + // const router = useRouter(); + // const { id } = router.query; + + const orderDetail = order.filter((o) =>o.id === Number(id)) + + + const { isOpen, onOpen, onClose } = useDisclosure(); + const [backdrop, setBackdrop] = useState("blur"); + const [size, setSize] = useState("3xl"); + + const handleOpen = (size) => { + // setBackdrop(backdrop); +setSize(size) + onOpen(); + }; + + return ( +
+ <> +
+ +
+ + + {(onClose) => ( + <> + +

Detalle - Orden de compra N°: {orderDetail[0]?.id}

+
+ +

+ Fecha: + {orderDetail[0]?.createdAt ? ( + orderDetail[0].createdAt.slice(0, 10) + ) : ( +

Se desconoce la fecha

+ )} +

+

Estados: {orderDetail[0]?.status.toUpperCase()}

+

Concepto: {orderDetail[0]?.concept.toUpperCase()}

+

Producto: {orderDetail[0]?.product}

+

Precio: ${orderDetail[0]?.paymentAmount}

+
+ + + + + )} +
+
+ +
+ ); +}; + +export default OrderDetail; diff --git a/front/components/userDashboard/PDFDownload.js b/front/components/userDashboard/PDFDownload.js new file mode 100644 index 00000000..e9e8d675 --- /dev/null +++ b/front/components/userDashboard/PDFDownload.js @@ -0,0 +1,68 @@ +import { jsPDF} from "jspdf"; +import "jspdf-autotable"; +// import image from "../../public/PROJUNITY.png"; + +export const PDFDownload = () => { + // Crear un nuevo documento PDF + const doc = new jsPDF("l"); + doc.setFont("helvetica"); + doc.setFontSize(30); + doc.text("Orden de compra", 145, 20, null, null, "center"); +// const imgData = image; +// doc.addImage(imgData, "PNG", 15, 40, 148, 210); + // Agregar contenido al PDF + +// doc.text("Orden de compra", 105, 15, 15, null, "center"); +// doc.addFont("/fonts/Pompiere-Regular.ttf", "Pompiere", "regular"); + doc.autoTable({ + startY: 28, + theme: "striped", + styles: { + fontSize: 5, + overflow: "linebreak", + cellPadding: 2, + lineColor: [0, 0, 0], + lineWidth: 0.2, + }, + headStyles: { + valign: "middle", + halign: "center", + fontSize: 15, + fillColor: [255, 255, 255], + textColor: [0, 0, 0], + }, + tableLineColor: [0, 0, 0], + tableLineWidth: 0.5, + columnStyles: { + 0: { + halign: "center", + }, + 1: { + halign: "center", + }, + 2: { + halign: "center", + }, + 3: { + halign: "center", + }, + 4: { + halign: "center", + }, + 5: { + halign: "left", + }, + }, + bodyStyles: { + fillColor: [255, 255, 255], + textColor: 0, + fontSize: 10, + minCellHeight: 15, + }, + head: [["Fecha", "N° Compra", "Estado", "Producto", "Precio", "desarrollador"]], + body: [["17/06/2023", "AE345TG", "CANCELADA", "LARAVEL", "$45", "Steve "]], + }); + + // Descargar el PDF + doc.save("OrdenDeCompra.pdf"); +}; diff --git a/front/components/userDashboard/ProyectosDesactivos.jsx b/front/components/userDashboard/ProyectosDesactivos.jsx new file mode 100644 index 00000000..1fc8d6a2 --- /dev/null +++ b/front/components/userDashboard/ProyectosDesactivos.jsx @@ -0,0 +1,39 @@ +import { useDispatch, useSelector } from "react-redux"; +import { useEffect, useState } from "react"; + +import { restoreProjects } from "../../redux/actions/actions"; +import { deleteProjects } from "../../redux/actions/actions"; + +import { Button } from "@nextui-org/react"; +import Swal from "sweetalert2"; + +const ProyectosDesactivos = () => { + + const dispatch = useDispatch(); + const deleted = useSelector((state) => state.projectsData.projectsRestore); +console.log(deleted); + + const active = () => { + dispatch(restoreProjects()); + Swal.fire({ + icon: "success", + title: "Proyecto activado con exito!", + showConfirmButton: true, + timer: 3500, + }); + + + }; + return ( +
+ +
+ ); +}; + +export default ProyectosDesactivos; diff --git a/front/components/userDashboard/analDashUser.jsx b/front/components/userDashboard/analDashUser.jsx index 33eeca87..f0689015 100644 --- a/front/components/userDashboard/analDashUser.jsx +++ b/front/components/userDashboard/analDashUser.jsx @@ -1,11 +1,64 @@ -import TableTotalProj from "./tableProjTotals"; +import { + Table, + TableHeader, + TableColumn, + TableBody, + TableRow, + TableCell, + getKeyValue, + Spinner, +} from "@nextui-org/react"; -const AnalDashUser = () => { +const AnalDashUser = ({ proj }) => { return (
-

Proyectos Totales

- + + + + Estado + + + Proyectos + + + Vistas + + + Rating + + + Estado + + + Ganancias + + + } + > + {proj?.map((p)=> + + + {!p.deletedAt ? "Activo" : "Desactivo"} + + {p.name} + {p.views} + {p.Ratings[0]?.score?p.Ratings[0]?.score : "0"} + {p.status.toUpperCase()} + {p.price <=0 ? "Free" : p.price} + + +)}; + +
); }; diff --git a/front/components/userDashboard/analitycs.jsx b/front/components/userDashboard/analitycs.jsx new file mode 100644 index 00000000..e26f8123 --- /dev/null +++ b/front/components/userDashboard/analitycs.jsx @@ -0,0 +1,27 @@ +import LayoutUser from "../layout/layoutUser.js"; +import Head from "next/head"; +import ChartsAnalitycs from "./chartsAnalitycs.jsx"; +import FilterAnalitycs from "./filterAnalitycs.jsx"; +import AnalDashUser from "./analDashUser.jsx"; +import { useSelector } from "react-redux"; + +const Analitycs = ({ proj }) => { + console.log(proj); + // const sesion = useSelector((state) => state.usersData.sesion); + // console.log(sesion); + return ( +
+

Estadisticas

+
+
+ {/* */} + + +
+ {/*

Proyectos mayor rendimiento por promoción

+

Solicitudes con mayor rendimiento

*/} +
+ ); +}; + +export default Analitycs; diff --git a/front/components/userDashboard/buttonEdit.jsx b/front/components/userDashboard/buttonEdit.jsx index edd943bf..1acd123c 100644 --- a/front/components/userDashboard/buttonEdit.jsx +++ b/front/components/userDashboard/buttonEdit.jsx @@ -8,7 +8,8 @@ import { Link } from "@nextui-org/react"; -const ButtonEdit = () => { +const ButtonEdit = ({id}) => { +// console.log(id); const { isOpen, onOpen, onOpenChange } = useDisclosure(); return (
@@ -27,7 +28,7 @@ const ButtonEdit = () => { {(onClose) => ( <> - ¿Quieres editar este proyecto? + ¿Quieres editar este proyecto? diff --git a/front/components/userDashboard/chartsAnalitycs.jsx b/front/components/userDashboard/chartsAnalitycs.jsx index 78e7da19..95e16ce9 100644 --- a/front/components/userDashboard/chartsAnalitycs.jsx +++ b/front/components/userDashboard/chartsAnalitycs.jsx @@ -1,6 +1,6 @@ import { - LineChart, - Line, + BarChart, + Bar, XAxis, YAxis, CartesianGrid, @@ -19,14 +19,23 @@ const data = [ { name: "Jun 27", views: 9 }, { name: "July 14", views: 26 }, ]; -const ChartsAnalitycs = () => { +const ChartsAnalitycs = ({ proj }) => { + console.log(proj); + + const categoriesByProject = proj?.map((p) => { + return { + name: p.name, + price: p.price <= 0 ?

Free

: p.price, + }; +}); + return (
- { - - +
); diff --git a/front/components/userDashboard/chartsViews.jsx b/front/components/userDashboard/chartsViews.jsx index ab77ec49..6cbb960a 100644 --- a/front/components/userDashboard/chartsViews.jsx +++ b/front/components/userDashboard/chartsViews.jsx @@ -10,11 +10,9 @@ const data = [ { name: "Jun", views: 0 }, { name: "July", views: 20 }, ]; -const Example = () => { -// export default class Example extends PureComponent { -// static demoUrl = 'https://codesandbox.io/s/line-chart-connect-nulls-sqp96'; - -// render() { +const Example = ({views}) => { + // const { views } = views; + // console.log(views.views); return (
{/* diff --git a/front/components/userDashboard/posts.jsx b/front/components/userDashboard/posts.jsx index 2a1bbe45..0ad607fe 100644 --- a/front/components/userDashboard/posts.jsx +++ b/front/components/userDashboard/posts.jsx @@ -17,7 +17,7 @@ const Posts = () => {

Comentarios

- + {/* */}
diff --git a/front/components/userDashboard/projDashUser.jsx b/front/components/userDashboard/projDashUser.jsx index 4d0a7c34..2364f84e 100644 --- a/front/components/userDashboard/projDashUser.jsx +++ b/front/components/userDashboard/projDashUser.jsx @@ -1,17 +1,37 @@ import ButtonEdit from "./buttonEdit"; -import {Button, Link} from "@nextui-org/react"; +import {Button, Link, Image} from "@nextui-org/react"; import React, { useState } from "react"; -const ProjDashUser = () => { - const [active, setActive] = useState(false); +// commentsAllowed: true; +// createdAt: "2023-10-23T22:44:25.525Z"; +// deletedAt: null; +// description: "Un flanger gratuito de la fantástica compañía Valhalla. Once algoritmos para obtener todo tipo de efectos que desafían cualquier descripción. A destacar los efectos que podemos conseguir automatizando los parámetros en nuestro DAW o mediante un teclado controlador MIDI."; +// id: 7; +// image: "https://val-media-offload.s3.amazonaws.com/wp-content/uploads/2016/06/08073920/ValhallaSpaceModGUI-960x437.jpg"; +// name: "Valhalla Space Modulator"; +// price: "0.00"; +// shortDescription: "Un flanger gratuito de la fantástica compañía Valhalla."; +// status: "Canceled"; +// updatedAt: "2023-10-23T22:44:25.525Z"; +// views: 25; +const ProjDashUser = ({proj}) => { + // console.log(proj); + + + // const [active, setActive] = useState(true); return (
-
+
+ {/* */} + +
+
+ {/*

{name}

*/}
diff --git a/front/package.json b/front/package.json index b173a0f2..b72b0763 100644 --- a/front/package.json +++ b/front/package.json @@ -21,6 +21,7 @@ "@nextui-org/react": "^2.1.13", "@react-email/link": "0.0.5", "axios": "^1.5.1", + "date-fns": "^2.30.0", "formik": "^2.4.5", "framer-motion": "^10.16.4", "jspdf": "^2.5.1", diff --git a/front/pages/browser.js b/front/pages/browser.js index 4630641c..5e3fa047 100644 --- a/front/pages/browser.js +++ b/front/pages/browser.js @@ -24,7 +24,7 @@ export default function Browser() { }, [dispatch]); const projects = useSelector((state) => state.projectsData.projectsFilter); console.log(projects); - /* const allprojects = useSelector((state) => state.projectsData.projects); */ + // const allprojects = useSelector((state) => state.projectsData.projects); const categories = useSelector((state) => state.projectsData.categories); const loading = useSelector((state) => state.projectsData.loading); @@ -182,7 +182,8 @@ export default function Browser() { handleFilterPrice("")} variant="bordered"> 🛒 $5 o menos - )} + ) + } {filtersActives.price !== 15 ? (
  • ) ) : (

    - No hay proyectos disponibles{" "} + No hay proyectos disponibles handleClearFilters()} diff --git a/front/pages/feed/posts.jsx b/front/pages/feed/posts.js similarity index 97% rename from front/pages/feed/posts.jsx rename to front/pages/feed/posts.js index b51a4f9f..15b45089 100644 --- a/front/pages/feed/posts.jsx +++ b/front/pages/feed/posts.js @@ -8,7 +8,7 @@ import Head from "next/head"; import Comments from "../../components/comments/comments"; import { getAllComments } from "../../redux/actions/actionsComment"; -import { comment } from "postcss"; + const Posts = () => { const dispatch = useDispatch(); diff --git a/front/pages/profile/analitycs.js b/front/pages/profile/analitycs.js deleted file mode 100644 index dec7c5ba..00000000 --- a/front/pages/profile/analitycs.js +++ /dev/null @@ -1,32 +0,0 @@ -import LayoutUser from "../../components/layout/layoutUser.js"; -import Head from "next/head"; -import ChartsAnalitycs from "../../components/userDashboard/chartsAnalitycs.jsx"; -import FilterAnalitycs from "../../components/userDashboard/filterAnalitycs.jsx"; -import AnalDashUser from "../../components/userDashboard/analDashUser.jsx"; -import { useSelector } from "react-redux"; - -const Analitycs = () => { - const sesion = useSelector((state) => state.usersData.sesion); - console.log(sesion); - return ( - - - ProjUnity | Analiticas {/* {sesion.name} */} - - -

    -

    Estadisticas

    -
    -
    - - - -
    -

    Proyectos mayor rendimiento por promoción

    -

    Solicitudes con mayor rendimiento

    -
    - - ); -}; - -export default Analitycs; diff --git a/front/pages/profile/index.js b/front/pages/profile/index.js index bfa83937..ec2df5f0 100644 --- a/front/pages/profile/index.js +++ b/front/pages/profile/index.js @@ -1,72 +1,126 @@ import { useRouter } from "next/router"; +// import ButtonEdit from "../../components/userDashboard/buttonEdit"; import { useDispatch, useSelector } from "react-redux"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import LayoutUser from "../../components/layout/layoutUser"; +import Loader from "../../components/layout/loader"; import Head from "next/head"; -import ProjDashUser from "../../components/userDashboard/projDashUser"; -import ButtonCreate from "../../components/userDashboard/buttonCreateProj"; -import ButtonPromotion from "../../components/userDashboard/buttonPromotion"; -import ButtonRequest from "../../components/userDashboard/buttonRequest"; -import Posts from "../../components/userDashboard/posts"; -import Example from "../../components/userDashboard/chartsViews"; -import DownloadCharts from "../../components/userDashboard/downloadCharts"; +// import ProjDashUser from "../../components/userDashboard/projDashUser"; +// import ButtonCreate from "../../components/userDashboard/buttonCreateProj"; +// import ButtonPromotion from "../../components/userDashboard/buttonPromotion"; +// import ButtonRequest from "../../components/userDashboard/buttonRequest"; +// import Posts from "../../components/userDashboard/posts"; +// import Example from "../../components/userDashboard/chartsViews"; +// import DownloadCharts from "../../components/userDashboard/downloadCharts"; import OrdenesCompra from "../../components/userDashboard/OrdenesCompra"; +import { getSesion } from "../../redux/actions/actionsUser"; +import { getProjects, deleteProjects } from "../../redux/actions/actions"; +// import { getOrder } from "../../redux/actions/actionsPayments"; +import { + Button, + Modal, + ModalContent, + ModalHeader, + ModalFooter, + useDisclosure, + Link, + Image, +} from "@nextui-org/react"; +import { + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, +} from "recharts"; +import Analitycs from "../../components/userDashboard/analitycs"; +import Buttons from "../../components/userDashboard/Buttons"; +import ProyectosDesactivos from "../../components/userDashboard/ProyectosDesactivos"; const Profile = () => { - const router = useRouter(); + // const router = useRouter(); + const { isOpen, onOpen, onOpenChange } = useDisclosure(); + + const dispatch = useDispatch(); + useEffect(() => { + dispatch(getSesion()); + dispatch(getProjects()); + }, [dispatch]); + const sesion = useSelector((state) => state.usersData.sesion); - console.log(sesion); + const projects = useSelector((state) => state.projectsData.projects); + const projectsByUser = projects?.filter((p) => Number(p.Users[0]?.id) === 2); //aca va sesion.id +console.log(projectsByUser); + const viewsByProject = projectsByUser?.map((p) => { + return { + name: p.name, + views: p.views ? ( + p.views + ) : ( +

    No se encuentran vistas para este proyecto

    + ), + }; + }); + console.log(viewsByProject); - // const userName = useSelector((state) => state.usersData.users); - // const projects = useSelector((state) => state.projectsData.projectsFilter); - // console.log(userName); - // const dispatch = useDispatch(); + const totalViews = viewsByProject.reduce( + (total, item) => total + item.views, + 0 + ); - // useEffect(() => { - // dispatch(getUserByName(name)); - // }, [dispatch]); + const ratingByProject = projectsByUser?.map((p) => { + return { + name: p.name, + rating: p.Ratings[0]?.score ? ( + p.Ratings[0]?.score + ) : ( +

    Este proyecto no se encuentra rankeado

    + ), + }; + }); + + if (!sesion?.id) return ; return ( - ProjUnity | {sesion.name} + ProjUnity | {sesion?.name}
    - - -
    -

    Panel de {sesion.name}

    +

    Panel de {sesion?.name}

    - - + {/* + */} - - - + + {/* + */}
    ViewsDownloadsFollowersDownloadsFollowers
    74512{totalViews}4512
    - - +

    @@ -74,73 +128,229 @@ const Profile = () => {

    - Promociones + Estadísticas

    -

    + {/*

    Solicitudes -

    -

    +

    */} + {/*

    Posts -

    -

    +

    */} + {/*

    Historial de Compras -

    +

    */}
    - -
    -

    Proyectos

    -
    -
    - - - -
    -
    -

    Summary

    - {/*

    View More

    */} -

    Vistas

    - -

    Descargas

    - + +
    +

    Proyectos

    + {/* */} +
    +
    + {projectsByUser.map((proj) => ( +
    +
    + {proj.name} +
    +
    +

    {proj.name}

    +
    +
    + {/* buton edit */} +
    + + + + {(onClose) => ( + <> + + ¿Quieres editar este proyecto? + + + + + + + + + + )} + + +
    +
    + + {/* + + */} + {/* + */} +
    +
    +
    +
    +
    + ))} + + + + {/* */} +
    +
    +

    Summary

    + + {/* GRAFICO DE VISTAS */} +

    Vistas

    + +
    + + + + + + + + + +
    + {/* GRAFICO DE RATING */} +

    Rating

    +
    + + + + + + + + + +
    +
    -
    - -
    -

    Promociones

    -
    -
    - - +
    + +
    + {/* + +
    +

    Promociones

    +
    +
    + + +
    +
    +

    Summary

    +

    View More

    +

    Vistas

    + +

    Descargas

    + +
    -
    -

    Summary

    - {/*

    View More

    */} -

    Vistas

    - -

    Descargas

    - +
    */} + {/*
    +

    Solicitudes

    +
    + +
    -
    -
    -
    -

    Solicitudes

    -
    - - -
    - -
    -
    -

    Posts

    - -
    -
    -

    Historial de compras

    - -
    + +
    */} + {/*
    +

    Posts

    + +
    */} + {/*
    +

    Historial de compras

    + +
    */}
    ); }; export default Profile; + +/* const [sesionProfile, setSesionProfile ] = useState({}) + sesion?.id && setSesionProfile({...sesion}) */ + + // const userName = useSelector((state) => state.usersData.users); + // const projects = useSelector((state) => state.projectsData.projectsFilter); + // console.log(userName); + // const dispatch = useDispatch(); + + // const loading = useSelector((state) => state.usersData.loading); + //* Aqui se maneja el loader + + diff --git a/front/pages/user/[id].js b/front/pages/user/[id].js index 12495832..a999ea87 100644 --- a/front/pages/user/[id].js +++ b/front/pages/user/[id].js @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState } from "react"; -import { getUserId } from "../../redux/actions/actionsUser"; +import { getUsers } from "../../redux/actions/actionsUser"; import LayoutUser from "../../components/layout/layoutUser"; import ProjectCardUser from "../../components/ProjectCardUser"; @@ -23,32 +23,31 @@ const Profile = () => { const dispatch = useDispatch(); const router = useRouter(); const id = router.query.id; - const userId = useSelector((state) => state.usersData.userId) - console.log(userId); + const projects = useSelector((state) => state.projectsData.projectsFilter); - + const projectsByUser = projects.filter((p) => Number(p.Users[0]?.id) === Number(id)); + + const users = useSelector((state) => state.usersData.users); + const user = users.filter(u => Number(u.id) === Number(id)); + + useEffect(() => { - dispatch(getUserId(id)) dispatch(getProjects()) - }, [dispatch, id]); + dispatch(getUsers()) + }, [dispatch]); return ( - ProjUnity | {userId.name} + ProjUnity | {user[0]?.name}
    - - {userId.name} + + {user[0]?.name}
    @@ -70,52 +69,47 @@ const Profile = () => {
    - {/* Usuario desde {userId.creationAt.slice(0,10)} */} -
    - - - + +
    - +
    - TOP 3 HR Influencers Spain ✪ FORBES Successful Entrepreneurs ✪ + {/* TOP 3 HR Influencers Spain ✪ FORBES Successful Entrepreneurs ✪ Co-Fundador ✪ Best Selling Author ✪ Mentor Marca Personal y Búsqueda de Empleo ✪ Ayudo a líderes a reclutar mejor con DATA ✪ - Tech-Blockchain-AI-Consultant + Tech-Blockchain-AI-Consultant */}

    Proyectos

    - {projects.slice(0, 5).map((proj) => ( + {projectsByUser?.map((proj) => ( ))}
    -
    -

    + {/*
    +

    Actividades recientes

    {projects.slice(0, 2).map((proj) => ( ))} -
    +

    */} ); diff --git a/front/public/PROJUNITY.png b/front/public/PROJUNITY.png new file mode 100644 index 00000000..87ad1726 Binary files /dev/null and b/front/public/PROJUNITY.png differ diff --git a/front/redux/actions/actions.js b/front/redux/actions/actions.js index 6e0987d5..f4e02e6e 100644 --- a/front/redux/actions/actions.js +++ b/front/redux/actions/actions.js @@ -13,21 +13,19 @@ import { LOGIN, GET_PREMIUM_PROJECT, ENDPOINT, + DELETE_PROJECTS, GET_COMMENTS_TO_DETAIL } from "../types"; const endpoint = ENDPOINT; export const getProjects = () => { - console.log(endpoint); - return async (dispatch) => { + return async (dispatch) => { try { const { data } = await axios(`${endpoint}projects`); - console.log(`${endpoint}projects`); - - console.log(data); + return dispatch({ type: GET_ALL_PROJECTS, payload: data }); } catch (error) { /* return dispatch({ @@ -197,3 +195,36 @@ export const updateProject = (data, id) => { } }; }; + +export const deleteProjects = (id) => { + console.log(id); + return async (dispatch) => { + try { + const { data } = await axios.delete(`${endpoint}projects/${id}`); +console.log(data); + return dispatch({ type: DELETE_PROJECTS, payload: data }); + } catch (error) { + /* return dispatch({ + type: SET_ALERT, + payload: { type: "error", msg: error.message }, + }); */ + console.log(error.message); + } + }; +}; + +export const restoreProjects = (idP) => { + return async (dispatch) => { + try { + const { data } = await axios.put(`${endpoint}projects/restore/${idP}`); + + return dispatch({ type: DELETE_PROJECTS, payload: data }); + } catch (error) { + /* return dispatch({ + type: SET_ALERT, + payload: { type: "error", msg: error.message }, + }); */ + console.log(error.message); + } + }; +}; diff --git a/front/redux/actions/actionsPayments.js b/front/redux/actions/actionsPayments.js new file mode 100644 index 00000000..b22c4310 --- /dev/null +++ b/front/redux/actions/actionsPayments.js @@ -0,0 +1,23 @@ +import axios from "axios"; + +import { GET_ORDER, ENDPOINT } from "../types"; + +const endpoint = ENDPOINT; + +export const getOrder = () => { + return async (dispatch) => { + try { + const response = await axios(`${endpoint}payment`); + console.log(response); + return dispatch({ + type: GET_ORDER, + payload: response.data, + }); + } catch (error) { + // return dispatch({ + // type: SET_ALERT, + // payload: { type: "error", msg: error.message }, + // }); + } + }; +}; diff --git a/front/redux/actions/actionsUser.js b/front/redux/actions/actionsUser.js index fd0007b1..daa6852d 100644 --- a/front/redux/actions/actionsUser.js +++ b/front/redux/actions/actionsUser.js @@ -1,6 +1,12 @@ import axios from "axios"; + +/* +const enpointLocal = "http://localhost:3001/"; */ +const enpointLocal = "https://projunity-production.up.railway.app/"; +const enpointApiNext = "http://localhost:3000/api/"; import { + GET_USERS, GET_USER_BY_ID, GET_USER_BY_NAME, LOGIN, @@ -12,15 +18,15 @@ import { const endpoint = ENDPOINT; -export const getUserId = (id) => { +export const getUsers = (id) => { return async (dispatch) => { try { - const response = await axios( - `https://api.escuelajs.co/api/v1/users/${id}` - ); + + const response = await axios(`${enpointLocal}users`); + return dispatch({ - type: GET_USER_BY_ID, + type: GET_USERS, payload: response.data, }); } catch (error) { diff --git a/front/redux/reducers/paymentReducer.js b/front/redux/reducers/paymentReducer.js new file mode 100644 index 00000000..76f1c3fb --- /dev/null +++ b/front/redux/reducers/paymentReducer.js @@ -0,0 +1,21 @@ +import { GET_ORDER } from "../types"; + +const initialState = { + payments: [], + loading: true, +}; + + const paymentReducer = (state = initialState, action) => { + switch (action.type) { + case GET_ORDER: + return { + ...state, + payments: action.payload, + loading: false, + } + default: + return state; + } + }; + + export default paymentReducer; \ No newline at end of file diff --git a/front/redux/reducers/projectsReducer.js b/front/redux/reducers/projectsReducer.js index b51d784b..37ec0da4 100644 --- a/front/redux/reducers/projectsReducer.js +++ b/front/redux/reducers/projectsReducer.js @@ -10,6 +10,8 @@ import { ORDER_CATEGORIES, GET_PROJECTS_BY_NAME, GET_PREMIUM_PROJECT, + DELETE_PROJECTS, + RESTORE_PROJECTS, GET_COMMENTS_TO_DETAIL } from "../types"; @@ -22,7 +24,9 @@ const initialState = { test: {}, alert: {}, detail: [], + projectsDesactive: [], comments: [], + projectsRestore: [], }; const projectsReducer = (state = initialState, action) => { @@ -127,6 +131,18 @@ const projectsReducer = (state = initialState, action) => { : true) ), }; + case DELETE_PROJECTS: + return { + ...state, + projectsDesactive: action.payload, + }; + + case RESTORE_PROJECTS: + return { + ...state, + projectsRestore: action.payload, + }; + default: return state; } diff --git a/front/redux/reducers/reducer.js b/front/redux/reducers/reducer.js index 3a65237b..89ef52bc 100644 --- a/front/redux/reducers/reducer.js +++ b/front/redux/reducers/reducer.js @@ -3,6 +3,7 @@ import projectsReducer from "./projectsReducer"; import usersReducer from "./usersReducer"; import carritoReducer from "./carritoReducer"; import commentsReducer from "./commentsReducer"; +import paymentReducer from "./paymentReducer"; import userDashboardReducer from "./dashboardReducer"; @@ -11,5 +12,6 @@ export default combineReducers({ carritoData: carritoReducer, usersData: usersReducer, commentData: commentsReducer, + paymentData: paymentReducer, userDashboard: userDashboardReducer, }); diff --git a/front/redux/reducers/usersReducer.js b/front/redux/reducers/usersReducer.js index 587215e6..d8c1aa5d 100644 --- a/front/redux/reducers/usersReducer.js +++ b/front/redux/reducers/usersReducer.js @@ -1,6 +1,6 @@ import { GET_SESION, - GET_USER_BY_ID, + GET_USERS, GET_USER_BY_NAME, LOGIN, LOGOUT, @@ -15,12 +15,13 @@ const initialState = { alert: {}, }; + const usersReducer = (state = initialState, action) => { switch (action.type) { - case GET_USER_BY_ID: + case GET_USERS: return { ...state, - userId: action.payload, + users: action.payload, loading: false, }; case SET_ALERT: diff --git a/front/redux/types.js b/front/redux/types.js index 8bc06986..3692bf80 100644 --- a/front/redux/types.js +++ b/front/redux/types.js @@ -9,9 +9,11 @@ export const FILTERS = "FILTERS"; export const ORDER_CATEGORIES = "ORDER_CATEGORIES"; export const FILTER_CLEAR = "FILTER_CLEAR"; export const GET_PREMIUM_PROJECT = "GET_PREMIUM_PROJECT"; +export const DELETE_PROJECTS = "DELETE_PROJECTS"; export const GET_COMMENTS_TO_DETAIL = "GET_COMMENTS_TO_DETAIL"; export const GET_PROJECTS_BY_NAME = "GET_PROJECTS_BY_NAME"; +export const RESTORE_PROJECTS = "RESTORE_PROJECTS"; /* export const GET_PROJECTS_BY_NAME = "GET_PROJECTS_BY_NAME"; */ /* export const GET_USER_BY_ID = "GET_USER_BY_ID"; */ @@ -24,7 +26,6 @@ export const REMOVE_ITEM = "REMOVE_ITEM"; export const GET_ALL_ITEMS = "GET_ALL_ITEMS"; //type users -export const GET_USER_BY_ID = "GET_USER_BY_ID"; export const LOGIN = "LOGIN"; export const LOGOUT = "LOGOUT"; export const GET_SESION = "GET_SESION"; @@ -42,6 +43,10 @@ export const RESTORE_USER = "RESTORE_USER" export const PAYMENT_RECORD = "PAYMENT_RECORD" //endpoint -// export const ENDPOINT = "http://localhost:3001/"; - export const ENDPOINT = "https://projunity-production.up.railway.app/"; +//export const ENDPOINT = "http://localhost:3001/"; +export const ENDPOINT = "https://projunity-production.up.railway.app/"; + + +//payments +export const GET_ORDER = "GET_ORDER" \ No newline at end of file diff --git a/server/index.js b/server/index.js index b2d44704..c5c0d22b 100644 --- a/server/index.js +++ b/server/index.js @@ -34,25 +34,25 @@ const { } = require("./src/db"); const { createUser } = require("./src/services/Users"); conn - .sync({ force: true }) + .sync({ force: false }) .then(() => { server.listen(PORT, async () => { - await UserTypes.bulkCreate(userTypes); - for (let i in users) { - await createUser(users[i]); - } - await Projects.bulkCreate(projects.data); - await Category.bulkCreate(categories); - await Comments.bulkCreate(comments); - await ProjectComments.bulkCreate(commentsProject); - await Ratings.bulkCreate(ratings); - await ProjectRatings.bulkCreate(projectsRatings); - await Payments.bulkCreate(payments); - await Tags.bulkCreate(tags); - await Ratings.bulkCreate(ratings); - await ProjectUser.bulkCreate(projectUser.data); - await ProjectCategory.bulkCreate(projectCategory); - await ProjectTags.bulkCreate(projectTags); + // await UserTypes.bulkCreate(userTypes); + // for (let i in users) { + // await createUser(users[i]); + // } + // await Projects.bulkCreate(projects.data); + // await Category.bulkCreate(categories); + // await Comments.bulkCreate(comments); + // await ProjectComments.bulkCreate(commentsProject); + // await Ratings.bulkCreate(ratings); + // await ProjectRatings.bulkCreate(projectsRatings); + // await Payments.bulkCreate(payments); + // await Tags.bulkCreate(tags); + // await Ratings.bulkCreate(ratings); + // await ProjectUser.bulkCreate(projectUser.data); + // await ProjectCategory.bulkCreate(projectCategory); + // await ProjectTags.bulkCreate(projectTags); console.log(`Server listening on port ${PORT}`); }); diff --git a/server/package-lock.json b/server/package-lock.json index 08ee94ec..6d1c5b6f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -21,9 +21,11 @@ "express-session": "^1.17.3", "fs-extra": "^11.1.1", "json-server": "^0.17.3", + "jsonwebtoken": "^9.0.2", "mercadopago": "^1.5.17", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.7", "nodemon": "^2.0.22", "npm-run-all": "^4.1.5", "passport": "^0.6.0", @@ -614,6 +616,11 @@ "node": ">=8" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -1137,6 +1144,14 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3145,6 +3160,46 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/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/jsonwebtoken/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -3173,6 +3228,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -3244,11 +3318,46 @@ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3531,6 +3640,14 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "node_modules/nodemailer": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "2.0.22", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", diff --git a/server/package.json b/server/package.json index c0ebe33d..44bec01f 100644 --- a/server/package.json +++ b/server/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "Server Proyecto Final", "main": "index.js", -"scripts": { + "scripts": { "start": "node ./index.js", "dev": "nodemon -L" }, @@ -22,9 +22,11 @@ "express-session": "^1.17.3", "fs-extra": "^11.1.1", "json-server": "^0.17.3", + "jsonwebtoken": "^9.0.2", "mercadopago": "^1.5.17", "morgan": "^1.10.0", "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.7", "nodemon": "^2.0.22", "npm-run-all": "^4.1.5", "passport": "^0.6.0", diff --git a/server/src/controllers/dashboard.js b/server/src/controllers/dashboard.js index e95756be..bf1b9a92 100644 --- a/server/src/controllers/dashboard.js +++ b/server/src/controllers/dashboard.js @@ -4,20 +4,32 @@ const dashBoardController = { getUserDashboard: async function (req,res) { try { const { id } = req.params - const { fecha } = req.query - let date = fecha? fecha.split('-') : [] - if (date.length !== 3) { - const currentTime = new Date() - date = new Date(currentTime.getFullYear(),currentTime.getMonth(),1,0,0,0) //<<--- si no esta definida la fecha desde, se define por defecto desde el primero del corriente mes + const { ventas, desde, hasta } = req.query + + const currentTime = new Date() + + let fechaDesde = desde? desde.split('-') : []; + fechaDesde.length !== 3? + fechaDesde = new Date(currentTime.getFullYear(),currentTime.getMonth(),1,0,0,0) + : fechaDesde = new Date(parseInt(desde[0]),parseInt(desde[1])-1,parseInt(desde[2]),0,0,0); //<<--- si no esta definida la fecha desde, se define por defecto desde el primero del corriente mes + + let fechaHasta = hasta? hasta.split('-') : []; + fechaHasta.length !== 3? + fechaHasta = currentTime + : fechaHasta = new Date(parseInt(hasta[0]),parseInt(hasta[1])-1,parseInt(hasta[2]),0,0,0); + let data; + if (ventas) { + data = await Services.userSalesDetail(parseInt(id), fechaDesde, fechaHasta) } else { - date = new Date(date[0],date[1]-1,date[2],0,0,0) + data = await Services.Dashboard(parseInt(id), fechaDesde); + } + if (data) { + res.status(200).json(data) } - const data = await Services.Dashboard(parseInt(id), date) - res.status(200).json(data) } catch (error) { res.status(500).json({message: error.message}) } - } + }, } -module.exports = dashBoardController \ No newline at end of file +module.exports = dashBoardController diff --git a/server/src/controllers/form.js b/server/src/controllers/form.js index 7ce4c1c9..0dbde58d 100644 --- a/server/src/controllers/form.js +++ b/server/src/controllers/form.js @@ -1,4 +1,6 @@ const { ProjectServices } = require("../services"); +const {sendEmail} = require("./mailer"); +const { Users } = require('../db'); const formControllers = { createNewProject: async function (req, res) { @@ -7,14 +9,31 @@ const formControllers = { const post = { ...projectData, }; - console.log(projectData); const newProject = await ProjectServices.createProjects(post); - if (newProject.id) { - res.status(201).json(newProject); - } else { - res.status(400).json({ type: "error", response: "Algo falló" }); - } + + const user = await Users.findByPk(projectData.userId); + + + const userMail = user.email + const subject = "¡Tu proyecto ha sido creado con éxito! 🚀"; + const text = `Querid@ ${user.name} Tu proyecto se ha creado con éxito. Esto es un gran paso hacia la realización de tu visión.! ` + const html = `

    + Querid@ ${user.name}, +

    + +

    Tu proyecto se ha creado con éxito. Esto es un gran paso hacia la realización de tu visión.!

    +

    Si deseas realizar cambios o actualizar la información de tu proyecto, puedes hacerlo desde tu panel de control

    +

    No dudes en ponerte en contacto con nuestro equipo de soporte si necesitas ayuda o tienes preguntas adicionales.

    +

    ¡Gracias por ser parte de nuestra comunidad y por compartir tu proyecto con nosotros!

    +

    Saludos,

    +
    +

    El equipo de ProJunity

    +

    @2023 ProJunity. Todos los derechos reservados.

    + ` + sendEmail(userMail, subject, text, html) + + res.status(200).json(newProject); console.log(newProject); } catch (error) { res.status(500).json(error.message); diff --git a/server/src/controllers/mailer.js b/server/src/controllers/mailer.js new file mode 100644 index 00000000..bdbdfbc6 --- /dev/null +++ b/server/src/controllers/mailer.js @@ -0,0 +1,35 @@ +const nodemailer = require('nodemailer'); + +const { + MAIL_USERNAME, + MAIL_PASSWORD +} = process.env + +const transporter = nodemailer.createTransport({ + host: "smtp.gmail.com", + port:465, + secure:true, + auth:{ + user: MAIL_USERNAME, + pass: MAIL_PASSWORD + } +}); + +const sendEmail = async(to, subject, text, html)=>{ + const mailOptions={ + from: MAIL_USERNAME, + to, + subject, + text, + html + }; + + try{ + await transporter.sendMail(mailOptions); + console.log(`Correo electrónico enviado a ${to}`); + }catch (error){ + console.log("Error al enviar el correo electrónico", error); + } +} + +module.exports = {sendEmail} diff --git a/server/src/controllers/mercadopago.js b/server/src/controllers/mercadopago.js index 2bfd6587..cb047357 100644 --- a/server/src/controllers/mercadopago.js +++ b/server/src/controllers/mercadopago.js @@ -1,149 +1,144 @@ const mercadopago = require("mercadopago"); const { MP_TOKEN, DB_HOST, CLIENT_HOST } = process.env; -const { Order_detail, Order, Payments, Users } = require("../db.js"); +// const { Order_detail, Order, Payments, Users } = require("../db.js"); const paymentsServices = require("../services/payment.js"); const { Sequelize } = require("sequelize"); const { projects } = require("../utils/index.js"); +// const mercadopago = require('mercadopago'); + +const { Order_detail , Order, Payments, Users, Projects } = require('../db.js'); +// const paymentsServices = require('../services/payment.js'); +// const {Sequelize} = require('sequelize'); // Configura las credenciales de MercadoPago const paymenntsControllers = { - // Función para crear una preferencia de pago en MercadoPago - createPaymentPreference: async function (req, res) { - // const { items, payer, concepto, status } = req.body; + + // Función para crear una preferencia de pago en MercadoPago + createPaymentPreference: async function(req, res ) { + mercadopago.configure({ access_token: MP_TOKEN, }); - //const id_orden= 1 - const orderNumber = await Payments.findAll({ - attributes: [Sequelize.fn("max", Sequelize.col("orderNumber"))], - raw: true, - }); - compra = req.body; - items = compra.map((item) => { - return { - buyer: item.buyer, - id: item.id, - title: item.title, - currency_id: "ARS", - unit_price: Number(item.unit_price), - quantity: 1, - }; - }); - /* [ - { - buyer:compra[0].UserId, - id:compra[0].projectId, - title: compra[0].title, - currency_id: 'ARS', - unit_price:Number(compra[0].unit_price), - quantity: 1, - - }, - { - buyer:compra[1].UserId, - id:compra[1].projectId, - title: compra[1].title, - currency_id: 'ARS', - unit_price:Number(compra[1].unit_price), - quantity: 1, - -}, -{ - buyer:compra[2].UserId, - id:compra[2].projectId, - title: compra[2].title, - currency_id: 'ARS', - unit_price:Number(compra[2].unit_price), - quantity: 1, - -}, - ]*/ -console.log(items); + + const lastOrderNumber = await Payments.findAll({ + attributes: [Sequelize.fn('max', Sequelize.col('orderNumber'))], + raw: true + }) + + const orderNumber = lastOrderNumber[0].max+1 + let items = req.body + for (let i in items) { + const createOrder = await Payments.create({ + paymentAmount: items[i].unit_price, + orderNumber: orderNumber, + product: items[i].id, + buyer: items[i].buyer, + concept: items[i].concept? items[i].concept : 'venta', //venta, donacion o devolucion + status: items[i].status? items[i].status : 'processing', + }) + } + + const totalPrecio = req.body.reduce((acumulador, producto) => + acumulador + parseFloat(producto.unit_price), 0); + const preference = { items, - total_amount: 1, - external_reference: `${orderNumber[0].max + 1}`, + total_amount: totalPrecio, + external_reference : `${orderNumber}`, + payer: await Users.findOne({ + where: {id: items[0].buyer}, + attributes: ['name', 'email'], + raw: true + }), + back_urls: { - success: "http://localhost:3001/createPayment/succes", - pending: `${CLIENT_HOST}/error`, - failure: `${CLIENT_HOST}/pending`, + success: "https://proj-unity.vercel.app", + pending: `${DB_HOST}/error`, + failure: `${DB_HOST}/pending`, }, notification_url: "https://3eb3-181-29-72-133.ngrok.io/webhook", auto_return: "approved", }; - // console.log(preference) - try { - const response = await mercadopago.preferences.create(preference); - // console.log(response.body); - global.id = response.body.id; - init_point = response.body.init_point; - const totalPrecio = items.reduce( - (acumulador, producto) => acumulador + parseFloat(producto.unit_price), - 0 - ); + try { + const response = await mercadopago.preferences.create(preference); + //console.log(response.body); - console.log(items); + global.id = response.body.id; + init_point = response.body.init_point; + projects = response.body.items.map(e=>{ + return{ + id:e.id, + price:e.unit_price + } + }) - const orderDb = await Payments.create({ - paymentId: global.id, - status: "created", - orderNumber: orderNumber[0].max + 1, - buyer: 1, - projects: 1, - paymentAmount: totalPrecio, - }); - // console.log(orderDb) - res.json({ - id: global.id, - init_point: response.body.init_point, - orderDb, - }); - } catch (error) { - console.log(error); - } - }, + for (let i in projects) { + await Payments.update( + { + paymentId: global.id, + status:"created", + }, + { + where: { + orderNumber: orderNumber, + product: projects[i].id + } + }); + } + const queryOrder = await Payments.findAll({where: {orderNumber: orderNumber}, raw: true}) + let itemsDb = [] + for (let i in queryOrder) { + let { product, paymentAmount} = queryOrder[i] + let productName = await Projects.findOne({where: {id: product}, attributes: ['name'], raw: true}) + itemsDb = [ + ...itemsDb, + { + id: product, + title: productName.name, + unit_price: paymentAmount, + quantity: 1 + } + ] + } - // falta relacionar las nuevas compras con projectId y userId - - // const items = [ - // { - // id: projectId, - // title: title, - // unit_price:Number(unit_price), - // quantity: 1 - // }, - // ] - // const totalPrecio = items.reduce((acumulador, producto) => - // acumulador + parseFloat(producto.unit_price), 0); - - // res.json({ - // id_mercadopago: global.id, - // init_point: response.body.init_point, - // items: response.body.items, - // back_urls: response.body.back_urls, - // total_amount:totalPrecio - // }); + res.json({id: global.id, init_point: response.body.init_point, itemsDb}) + + } catch (error) { + console.log(error); + } + }, - getOrdenId: async function (req, res) { - try { - const { id } = req.params; - const payment = await paymentsServices.paymentId(id); - res.status(200).json(payment); + getOrdenId: async function(req, res){ + try { + const {id} = req.params + const payment = await paymentsServices.paymentId(id); + res.status(200).json(payment); } catch (error) { - res.status(500).json(error.message); + res.status(500).json(error.message); } }, - getAllPayment: async function (req, res) { + getAllPayment: async function(req, res){ try { - const paymentsData = req.body; - const allPayments = await paymentsServices.allPayments(paymentsData); - res.status(200).json(allPayments); + const { desde, hasta } = req.query + const currentTime = new Date() + let fechaDesde = desde? desde.split('-') : []; + fechaDesde.length !== 3? + fechaDesde = new Date(currentTime.getFullYear(),currentTime.getMonth(),1,0,0,0) + : fechaDesde = new Date(parseInt(desde[0]),parseInt(desde[1])-1,parseInt(desde[2]),0,0,0); //<<--- si no esta definida la fecha desde, se define por defecto desde el primero del corriente mes + + let fechaHasta = hasta? hasta.split('-') : []; + fechaHasta.length !== 3? + fechaHasta = currentTime + : fechaHasta = new Date(parseInt(hasta[0]),parseInt(hasta[1])-1,parseInt(hasta[2]),0,0,0); + + const allPayments = await paymentsServices.allPayments({...req.query, desde: fechaDesde, hasta: fechaHasta}); + res.status(200).json(allPayments) } catch (error) { - res.status(500).json(error.message); + res.status(500).json(error.message) } - }, + } }; - -module.exports = paymenntsControllers; + +module.exports = paymenntsControllers diff --git a/server/src/controllers/projects.js b/server/src/controllers/projects.js index 8ddc6127..8806191f 100644 --- a/server/src/controllers/projects.js +++ b/server/src/controllers/projects.js @@ -4,63 +4,63 @@ const projectControllers = { getProjects: async function (req, res) { try { const { id } = req.params; - if (id && Number.isNaN(Number(id))) { - res.status(401).send("Project ID is not a valid number"); - } else { - const projectsFilter = await Services.allProjects({ ...req.query }); - res.status(200).json(projectsFilter); + if (id && Number.isNaN(Number(id))) { + res.status(401).send('Project ID is not a valid number'); + } else { + const projectsFilter = await Services.allProjects({...req.query}) + res.status(200).json(projectsFilter) + } + } catch (error){ + res.status(500).json(error.message) + } +}, +getProjectsID: async function (req,res) { + try { + const {id} = req.params + const projectDetail = await Services.projectId(id) + res.status(200).json(projectDetail) + } catch (error) { + res.status(500).json(error.message) + } +}, + putProjects: async function (req, res) { + try { + const projectId = req.params.id; + const projectData = req.body; + + const updatedProject = await Services.updateProject( + projectId, + projectData + ); + + res.status(200).json(updatedProject); + console.log(updatedProject); + } catch (error) { + res.status(500).json(error.message); } - } catch (error) { - res.status(500).json(error.message); - } - }, - getProjectsID: async function (req, res) { - try { - const { id } = req.params; - const projectDetail = await Services.projectId(id); - res.status(200).json(projectDetail); - } catch (error) { - res.status(500).json(error.message); - } - }, - putProjects: async function (req, res) { - try { - const projectId = req.params.id; - const projectData = req.body; - - const updatedProject = await Services.updateProject( - projectId, - projectData - ); - - res.status(200).json(updatedProject); - console.log(updatedProject); - } catch (error) { - res.status(500).json(error.message); - } - }, + }, - deleteProject: async function (req, res) { - try { - const projectId = req.params.id; - - const result = await Services.deleteProject(projectId); - - res.status(200).json(result); - } catch (error) { - res.status(500).json(error.message); - } - }, + deleteProject: async function (req, res) { + try { + const projectId = req.params.id; + + const result = await Services.deleteProject(projectId); + + res.status(200).json(result); + } catch (error) { + res.status(500).json(error.message); + } + }, - restoreProject: async function (req, res) { - try { - const projectId = req.params.id; - const result = await Services.restoreProjects(projectId); - res.status(200).json(result); - } catch (error) { - res.status(500).json(error.message); + restoreProject : async function (req, res) { + try{ + const projectId = req.params.id; + const result = await Services.restoreProjects(projectId); + res.status(200).json(result); + }catch(error){ + res.status(500).json(error.message); + } } - }, -}; +} module.exports = projectControllers; diff --git a/server/src/controllers/users.js b/server/src/controllers/users.js index c940df9c..83cf00ed 100644 --- a/server/src/controllers/users.js +++ b/server/src/controllers/users.js @@ -1,19 +1,49 @@ const Service = require('../services').userServices; +const {sendEmail} = require("./mailer"); const userControllers = { getUsers: async function (req,res) { try { - const { name } = req.query - const Users = await Service.allUsers(name) + const { name, deleted } = req.query + const Users = await Service.allUsers({name, deleted}) res.status(200).json(Users) } catch (error) { res.status(500).json(error.message) } }, + getUserById: async function(req,res) { + try { + const {id} = req.params; + const User = await Service.userById(id) + if (User) { + res.status(200).json(User) + } + } catch (error) { + res.status(500).json(error.message) + } + }, postUser: async function (req,res) { try { const Users = await Service.createUser(req.body) if (Users.id) { + + const userMail = Users.email + const subject = "Usuario creado con éxito ✔ 😉"; + const text = `Querid@ ${Users.name} Tu proyecto se ha creado con éxito. Felicitaciones y gracias por hacer de nuestra comunidad un lugar mejor! ` + const html = `

    + Querid@ ${Users.name}, +

    + +

    Muchas gracias por formar parte de nuestra comunidad. Esperamos que disfrutes de tu experiencia en nuestro sitio.

    +

    Te recordamos que podés ingresar a tu cuenta con tu email y la contraseña que elegiste al registrarte.

    +

    ¡Te esperamos pronto!

    +

    Saludos,

    +
    +

    El equipo de ProJunity

    +

    @2023 ProJunity. Todos los derechos reservados.

    + ` + sendEmail(userMail, subject, text, html) + res.status(201).json(Users); } else { res.status(400).json({ type: "error", response: "Algo falló" }); @@ -28,7 +58,7 @@ const userControllers = { try { const userId = req.params.id; - const result = await Service.deleteUser(userId); + const result = await Service.deleteUserbyId(userId); res.status(200).json(result); } catch (error) { @@ -38,8 +68,8 @@ const userControllers = { restoreUser : async function (req, res) { try{ - const userId = req.params.id; - const result = await Service.restoreUsers(userId); + const {id} = req.params; + const result = await Service.restoreUsers(id); res.status(200).json(result); }catch(error){ res.status(500).json(error.message); @@ -47,4 +77,4 @@ const userControllers = { } } -module.exports = userControllers \ No newline at end of file +module.exports = userControllers diff --git a/server/src/db.js b/server/src/db.js index b9ea4582..7701230d 100644 --- a/server/src/db.js +++ b/server/src/db.js @@ -12,10 +12,10 @@ const { -/* const sequelize = new Sequelize(`postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/projunity`, { - logging: false, - native: false, - }); */ +// const sequelize = new Sequelize(`postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}/projunity`, { +// logging: false, +// native: false, +// }); const sequelize = new Sequelize(DEPLOY, { logging: false, diff --git a/server/src/models/user.js b/server/src/models/user.js index 41b870cb..d7b0c6fe 100644 --- a/server/src/models/user.js +++ b/server/src/models/user.js @@ -61,4 +61,4 @@ module.exports = (sequelize) => { paranoid: true, freezeTableName: true }); -}; \ No newline at end of file +}; diff --git a/server/src/routes/auth.js b/server/src/routes/auth.js index a3bba29f..ba3a1d59 100644 --- a/server/src/routes/auth.js +++ b/server/src/routes/auth.js @@ -12,6 +12,12 @@ const salt = process.env.SALT_KEY; const JWT_KEY = process.env.JWT_KEY; +const { + + GITHUB_CB_URL +} = process.env; + + function encryptionPassword(password) { var key = pbkdf2.pbkdf2Sync( password, salt, 36000, 64, 'sha256' @@ -167,8 +173,9 @@ router.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (req,res,next) => { const token = jwt.sign({id: req.user.id}, JWT_KEY, {expiresIn: 60 * 60 * 24 * 1000}) req.logIn(req.user, function(err) { - if (err) return next(err); ; - res.redirect(`http://localhost:3000?token=${token}`) + if (err) return next(err); + console.log('res redirect ', req); + res.redirect(`https://proj-unity.vercel.app?token=${token}`) }); }, ); @@ -181,13 +188,11 @@ router.get('/profile', async (req, res) => { res.status(401).send({ error: "NotAuthorized" }) } else { req.user = data - Users.findOne({ where: {id: req.user.id}, attributes: {exclude: ['password']}, raw: true }).then((user) => { - console.log("user auth es", user) res.status(200).json(user) }); } @@ -195,7 +200,6 @@ router.get('/profile', async (req, res) => { }) - router.get('/logout', function(req, res) { if(req.isAuthenticated()){ req.logOut(function(err) { @@ -207,4 +211,4 @@ router.get('/logout', function(req, res) { } }); -module.exports = router \ No newline at end of file +module.exports = router diff --git a/server/src/routes/index.js b/server/src/routes/index.js index aca2724e..71769419 100644 --- a/server/src/routes/index.js +++ b/server/src/routes/index.js @@ -33,20 +33,20 @@ function isAuthorized(req, res, next) { router.get("/", isAuthenticated); +router.route('/users/:id') + .get(Controller.getUserById) + .delete(isAuthenticated, isAuthorized, Controller.deleteUser); -router.delete('/users/:id',isAuthenticated, isAuthorized, Controller.deleteUser) +router.put('/users/restore/:id',isAuthenticated, isAuthorized, Controller.restoreUser); -router.put('/users/restore/:id',isAuthenticated, isAuthorized, Controller.restoreUser) - -router.get('/users/:id/dashboard', Controller.getUserDashboard) +router.get('/users/:id/dashboard', Controller.getUserDashboard); router.post("/sign-up", Controller.postUser); router.route('/users') .get(Controller.getUsers); -/* router.post('/sign-up', Controller.postUser); */ router.get( "/usertypes", @@ -65,16 +65,12 @@ router router .route("/projects/:id") + .get(Controller.getProjectsID) .put(Controller.putProjects) - .delete( Controller.deleteProject) + .delete(Controller.deleteProject); router.put('/projects/restore/:id', Controller.restoreProject) - - -router.get('/projects/:id', Controller.getProjectsID); - - router.get("/categories", Controller.getCategories); router.get("/tags", Controller.getTags); @@ -95,13 +91,11 @@ router router.get("/payment/:id", Controller.getOrdenId); router.get("/payment",Controller.getAllPayment) router -.route("/createPayment/succes") +.route("/payment/succes") .get((req, res)=> { - res.send("PAGO REALIZADO CON EXITO") + res.redirect('https://proj-unity.vercel.app/') + //res.send('PAGO REALIZADO CON EXITO') }) module.exports = router; - - - diff --git a/server/src/services/Users.js b/server/src/services/Users.js index f4477446..52f9cdc8 100644 --- a/server/src/services/Users.js +++ b/server/src/services/Users.js @@ -20,191 +20,149 @@ function encryptionPassword(password) { } const userServices = { - allUsers: async function (name) { - try { - if (name) { - const response = await Users.findAll({ - where: { - name: { [Op.iLike]: `%${name}%` }, - [Op.or]: [{ name: { [Op.iLike]: `${name}%` } }], - [Op.and]: [{ active: "true" }], - }, - attributes: [ - "id", - "name", - "email", - "image", - "twitterUser", - "emailUser", - "githubUser", - "linkedinUser", - "role", - ], - }); - return response; - } else { - const response = await Users.findAll({ - where: { active: "true" }, - attributes: [ - "id", - "name", - "email", - "image", - "twitterUser", - "emailUser", - "githubUser", - "linkedinUser", - "role", - ], - }); - return response; - } - } catch (error) { - return error; - } - }, - createUser: async function (userData) { - try { - const { - name, - email, - password, - image, - twitterUser, - emailUser, - githubUser, - linkedinUser, - role, - } = userData; + allUsers: async function (queryParams) { + try{ + const { name, deleted } = queryParams - if ( - !name || - !email || - !password /* || !image || !twitterUser || !emailUser || !githubUser <<== MODIFIQUE ESTO PARA PODER CREAR USUARIOS */ || - !role - ) { - throw Error(`Missing some data`); - } else { - if (image) { - const uploadedImage = await cloudinary.uploader.upload(image); + const response = await Users.findAll({ + where: name? { + name: { [Op.iLike]: `%${name}%` }, + [Op.or]: [{ name: { [Op.iLike]: `${name}%` } }], + } : null, + paranoid: deleted? false : true, + attributes: {exclude: ['password']}, + }) + return response + } catch (error) { + return error } - - const [newUser, created] = await Users.findOrCreate({ - where: { email: email }, - defaults: { - name, - password: encryptionPassword(password), - image: null, - /* image, */ - twitterUser, - emailUser, - githubUser, - role, - linkedinUser, - }, + }, + userById: async function(id) { + try { + const userId = await Users.findOne({ + where: {id: id}, + attributes: {exclude: ['password']} }); - if (created) { - let { - id, - name, - email, - image, - twitterUser, - emailUser, - githubUser, - linkedinUser, - role, - } = newUser; - return { - id, - name, - email, - image, - twitterUser, - emailUser, - githubUser, - linkedinUser, - role, - }; + if (userId) { + return userId } else { - throw Error("El email de usuario ya existe"); + return 'User not Found' } + } catch (error) { + return error } - } catch (error) { - return error; - } - }, - - updateUser: async function (userData, res) { - try { - const { - id, - name, - email, - password, - image, - twitterUser, - emailUser, - githubUser, - linkedinUser, - roleId, - } = userData; - // find the user by ID - const user = await Users.findByPk(id); - if (!user) { - throw new Error("User not found"); - } - // update the user data - user.name = name || user.name; - user.email = email || user.email; - user.password = password ? encryptionPassword(password) : user.password; - user.twitterUser = twitterUser || user.twitterUser; - user.emailUser = emailUser || user.emailUser; - user.githubUser = githubUser || user.githubUser; - user.linkedinUser = linkedinUser || user.linkedinUser; - user.roleId = roleId || user.roleId; - - // upload the image to Cloudinary - if (image) { - const uploadedImage = await cloudinary.uploader.upload(image); - user.image = uploadedImage.secure_url; - } + }, + createUser: async function (userData) { + try { + const { name, email, password, image, twitterUser, emailUser, githubUser, linkedinUser, role} = userData - // save the changes - await user.save(); + if ( !name || !email || !password /* || !image || !twitterUser || !emailUser || !githubUser <<== MODIFIQUE ESTO PARA PODER CREAR USUARIOS */ || !role) { - // return the updated user object - res.status(200).json(user); - } catch (error) { - console.log(error); - return error; - } - }, - deleteUser: async function (userId) { - try { - const user = await Users.findByPk(userId); - if (!user) { - throw new Error("User not found"); - } - await user.destroy(); - return { message: "User deleted successfully" }; - } catch (error) { - throw new Error(error.message); - } - }, + throw Error(`Missing some data`) + } else { + let uploadedImage; + if (image) { + uploadedImage = await cloudinary.uploader.upload(image); + } + const [newUser, created] = await Users.findOrCreate({ + where: {email: email}, + defaults: { + name, + email, + password: encryptionPassword(password), + image: uploadedImage? uploadedImage.secure_url : null, + twitterUser, + emailUser, + githubUser, + role, + linkedinUser + } + }) + if (created) { + let { id, name, email, image, twitterUser,emailUser, githubUser, linkedinUser, role } = newUser + return { id, name, email, image, twitterUser, emailUser, githubUser, linkedinUser, role } + } else { + throw Error('El email de usuario ya existe') + } + } + } catch (error){ + return error + } + }, + +updateUser: async function (userData, res) { + try { + const { id, name, email, password, image, twitterUser, emailUser, githubUser, linkedinUser, roleId} = userData + // find the user by ID + const user = await Users.findByPk(id); + if (!user) { + throw new Error("User not found"); + } + // update the user data + user.name = name || user.name; + user.email = email || user.email; + user.password = password ? encryptionPassword(password) : user.password; + user.twitterUser = twitterUser || user.twitterUser; + user.emailUser = emailUser || user.emailUser; + user.githubUser = githubUser || user.githubUser; + user.linkedinUser = linkedinUser || user.linkedinUser; + user.roleId = roleId || user.roleId; + + // upload the image to Cloudinary + if (image) { + const uploadedImage = await cloudinary.uploader.upload(image); + user.image = uploadedImage.secure_url; + } + + // save the changes + await user.save(); + + // return the updated user object + res.status(200).json(user); + } catch (error) { + console.log(error); + return error; + } + }, + deleteUserbyId: async function(userId) { + try { + const user = await Users.findByPk(userId); + if (!user) { + throw new Error('User not found'); + } + await user.destroy(); + return { message: 'User deleted successfully' }; + } catch (error) { + throw new Error(error.message); + } + }, + + getDeletedUsers: async function () { + try { + const deletedUsers = await Users.findAll({ + paranoid: false, + attributes: {exclude: ['password']} + }); + + return deletedUsers; + } catch (error) { + throw new Error(error.message); + } + }, - restoreUsers: async function (userId) { - try { - const user = await Users.findByPk(userId, { paranoid: false }); - if (!user) { - throw new Error("User not found"); - } - await user.restore(); - return { message: "User restored successfully" }; - } catch (error) { - throw new Error(error.message); - } - }, -}; + restoreUsers: async function(userId) { + try { + const user = await Users.findOne({where: {id: userId}, paranoid: false}); + if (!user) { + throw new Error('User not found'); + } + await user.restore(); + return { message: 'User restored successfully' }; + } catch (error) { + throw new Error(error.message); + } + }, + +} -module.exports = userServices; +module.exports = userServices diff --git a/server/src/services/payment.js b/server/src/services/payment.js index 656d1d56..95e49249 100644 --- a/server/src/services/payment.js +++ b/server/src/services/payment.js @@ -1,21 +1,45 @@ -const { Projects, Payments} = require('../db.js'); // Importa tus modelos de órdenes +const { Projects, Payments, Users} = require('../db.js'); // Importa tus modelos de órdenes const { Op, Sequelize } = require('sequelize'); -const Controllers = require("./index.js") +const {format} = require('date-fns'); +const Controllers = require("./index.js"); const paymentsServices = { allPayments: async function(query) { try { - const { paymentId, status, paymentAmount, projects, UserId } = query; - const payments = await Payments.findAll({ - - - }); - const orderNumber = await Payments.findAll({ - attributes: [Sequelize.fn('max', Sequelize.col('orderNumber'))], + let { paymentId, status, paymentAmount, projects, UserId, desde, hasta } = query; + + const {count, rows} = await Payments.findAndCountAll({ + where: { + createdAt: {[Op.between]: [desde, hasta]}, + }, + include: { + model: Users, + attributes: ['name'] + }, + attributes: ['id','paymentId','paymentAmount','status','concept','orderNumber','createdAt','product'], + order: [['createdAt', 'DESC']], raw: true - }) - + }); + const projectsName = await Projects.findAll({attributes: ['id','name'], paranoid: false}) + + let payments = [] + for (let i in rows) { + payments = [ + ...payments, + { + id: rows[i].id, + paymentId: rows[i].paymentId, + paymentAmount: rows[i].paymentAmount, + status: rows[i].status, + concept: rows[i].concept, + orderNumber: rows[i].orderNumber, + product: projectsName.filter((x) => x.id === rows[i].product)[0].name, + buyer: rows[i]['User.name']? rows[i]['User.name'] : 'undefined', + createdAt: format(rows[i].createdAt, 'yyyy-MM-dd hh:mm') + } + ] + } return payments; } catch (error) { @@ -36,4 +60,4 @@ const paymentsServices = { }, // el create payment de mercado pago esta realizado desde /controllers/mercadopago.js }; -module.exports = paymentsServices; \ No newline at end of file +module.exports = paymentsServices; diff --git a/server/src/services/projects.js b/server/src/services/projects.js index 843e0ab0..3bc7b8c1 100644 --- a/server/src/services/projects.js +++ b/server/src/services/projects.js @@ -14,132 +14,139 @@ cloudinary.config({ const ProjectServices = { allProjects: async function (queryParams) { try { - const { name, category, tag, price, rating, username, id } = queryParams; + const { name, category, tag, price, rating, username, id, deleted } = queryParams; let condition = {}; - id + id ? (condition = { ...condition, - project: - {...condition.project, id: id} - }) + project: { ...condition.project, id: id }, + }) : null; name ? (condition = { - ...condition, - project: { - name: { [Op.iLike]: `%${name}%` }, - [Op.or]: [{ name: { [Op.iLike]: `${name}%` } }], - }, - }) + ...condition, + project: { + name: { [Op.iLike]: `%${name}%` }, + [Op.or]: [{ name: { [Op.iLike]: `${name}%` } }], + }, + }) : null; tag ? (condition = { - ...condition, - tag: { - name: { [Op.iLike]: `%${tag}%` }, - [Op.or]: [{ name: { [Op.iLike]: `${tag}%` } }], - }, - }) + ...condition, + tag: { + name: { [Op.iLike]: `%${tag}%` }, + [Op.or]: [{ name: { [Op.iLike]: `${tag}%` } }], + }, + }) : null; category ? (condition = { - ...condition, - category: { - name: { [Op.iLike]: `%${category}%` }, - [Op.or]: [{ name: { [Op.iLike]: `${category}%` } }], - }, - }) + ...condition, + category: { + name: { [Op.iLike]: `%${category}%` }, + [Op.or]: [{ name: { [Op.iLike]: `${category}%` } }], + }, + }) : null; price ? (condition = { - ...condition, - project: { - ...condition.project, - price: { - [Op.or]: { [Op.lt]: price, [Op.eq]: price }, - }, + ...condition, + project: { + ...condition.project, + price: { + [Op.or]: { [Op.lt]: price, [Op.eq]: price }, }, - }) + }, + }) : null; rating ? (condition = { - ...condition, - ratings: { - score: { - [Op.gte]: rating, + ...condition, + rating: { + score: { + [Op.or]: { + [Op.lt]: rating, + [Op.eq]: rating, }, - // [Op.or]: [{ score: { [Op.eq]: score } }], }, - }) - : null; + }, + }) + : null; + username + ? (condition = { + ...condition, + users: { + name: { [Op.iLike]: `%${username}%` }, + [Op.or]: [{ name: { [Op.iLike]: `${username}%` } }] + } + }) + : null; - if (Object.keys(condition).length !== 0) { - const projectsFilter = await Projects.findAll({ - include: [ - { - model: Category, - attributes: ["name"], - where: condition.category, - through: { attributes: [] }, - }, - { - model: Tags, - attributes: ["name"], - where: condition.tag, - through: { attributes: [] }, - }, - { - model: Ratings, - attributes: ["score", "comment"], - where: condition.rating, - through: { attributes: [] }, - }, - { - model: Users, - attributes: ["id", "name", "email"], - where: condition.users, - through: { attributes: [] }, - }, - ], - where: condition.project, - }); - return projectsFilter; + condition.project = { + ...condition.project, + }; + + const projectsFilter = await Projects.findAll({ + include: [ + { + model: Category, + attributes: ["name"], + where: condition.category, + through: { attributes: [] }, + }, + { + model: Tags, + attributes: ["name"], + where: condition.tag, + through: { attributes: [] }, + }, + { + model: Ratings, + attributes: ["score", "comment"], + where: condition.rating, + through: { attributes:[] } , + }, + { + model: Comments, + attributes: ['id', 'comment', 'replyTo'], + through: {attributes: []} + }, + { + model: Users, + attributes: ['id','name','email','githubUser','twitterUser','linkedinUser'], + where: condition.users, + through: {attributes: []} + } + ], + where: condition.project, + paranoid: false, + }); + let changeDeletedAt = [] + for (let i in projectsFilter) { + changeDeletedAt = [ + ...changeDeletedAt, + { + ...projectsFilter[i].get({plain:true}), + deletedAt: projectsFilter[i].deletedAt !== null? true : false, + } + ] + } + if (deleted) { + let deletedProjects = projectsFilter.filter((x) => x.deletedAt !== null) + return deletedProjects } else { - const allProject = await Projects.findAll({ - include: [ - { - model: Category, - attributes: ["name"], - through: { attributes: [] }, - }, - { - model: Tags, - attributes: ["name"], - through: { attributes: [] }, - }, - { - model: Ratings, - attributes: ["score", "comment"], - through: { attributes: [] }, - }, - { - model: Users, - attributes: ["id", "name", "email"], - /* where: condition.users, */ - through: { attributes: [] }, - }, - ], - }); - return allProject; + return changeDeletedAt } } catch (error) { return error; } }, - projectId: async function (id) { + + getProjectsByID: async function (id) { try { const ProjectId = await Projects.findOne({ - where: { id: id }, + where: { id: id}, include: [ { model: Category, @@ -164,17 +171,21 @@ const ProjectServices = { }, { model: Users, - attributes: ["id", "name", "email"], - /* where: condition.users, */ + attributes: [ + "id", + "name", + "email", + "githubUser", + "twitterUser", + "linkedinUser", + ], + where: condition.users, through: { attributes: [] }, }, ], + where: condition.project, }); - if (ProjectId) { - return ProjectId; - } else { - throw Error(`Id ${id} no encontrado`); - } + return projectsFilter; } catch (error) { return error; } @@ -202,7 +213,7 @@ const ProjectServices = { !price || !shortDescription || !image || - /* !commentsAllowed || */ + /* !commentsAllowed || */ !status || !category || !tags || @@ -222,7 +233,7 @@ const ProjectServices = { price: parseFloat(price), visibility: visibility /* === "true" ? true : false */, shortDescription, - image : uploadedImage.url, + image: uploadedImage.url, views: 0, commentsAllowed: commentsAllowed /* === "true" ? true : false */, status, @@ -315,25 +326,66 @@ const ProjectServices = { throw new Error("Project not found"); } await project.destroy(); - return { message: "Project deleted successfully" }; + return { message: 'Project deleted successfully' }; } catch (error) { throw new Error(error.message); } }, - restoreProjects: async function (projectId) { + + + getDeletedProjects: async function () { + try { + const deletedProjects = await Projects.findAll({ + where: { paranoid: false }, + include: [ + { + model: Category, + attributes: ["name"], + through: { attributes: [] }, + }, + { + model: Tags, + attributes: ["name"], + through: { attributes: [] }, + }, + { + model: Comments, + attributes: ["id", "comment", "replyTo"], + through: { attributes: [] }, + }, + { + model: Ratings, + attributes: ["score", "comment"], + /* where: condition.rating, */ + through: { attributes: [] }, + }, + { + model: Users, + attributes: ["id", "name", "email"], + /* where: condition.users, */ + through: { attributes: [] }, + }, + ], + }); + + return deletedProjects; + } catch (error) { + return error; + } +}, + + restoreProjects: async function(projectId) { try { - const project = await Projects.findByPk(projectId, { paranoid: false }); + const project = await Projects.findByPk(projectId, {paranoid: false}); if (!project) { throw new Error("Project not found"); } await project.restore(); - return { message: "Project restored successfully" }; + return { message: 'Project restored successfully' }; } catch (error) { throw new Error(error.message); } }, - - }; module.exports = ProjectServices; diff --git a/server/src/services/userDashboard.js b/server/src/services/userDashboard.js index 1991afb9..91a3611d 100644 --- a/server/src/services/userDashboard.js +++ b/server/src/services/userDashboard.js @@ -1,6 +1,6 @@ const {Users, UserTypes, Projects, Payments, Ratings, ProjectUser} = require('../db'); const {Op, Sequelize} = require('sequelize'); -const {format} = require('date-fns') +const {format, addDays} = require('date-fns') const DashboardService = { Dashboard: async function (id, fecha) { @@ -49,15 +49,15 @@ const DashboardService = { topProjectsData = topProjectsData.slice(0,3) topProjectsData = [ { - project: await Projects.findOne({where: {id: topProjectsData[0].product}, attributes: ['name'], raw: true}), + project: await Projects.findOne({where: {id: topProjectsData[0].product}, attributes: ['name'], raw: true, paranoid: false}), ventas: topProjectsData[0].Count }, { - project: await Projects.findOne({where: {id: topProjectsData[1].product}, attributes: ['name'], raw: true}), + project: await Projects.findOne({where: {id: topProjectsData[1].product}, attributes: ['name'], raw: true, paranoid: false}), ventas: topProjectsData[1].Count }, { - project: await Projects.findOne({where: {id: topProjectsData[2].product}, attributes: ['name'], raw: true}), + project: await Projects.findOne({where: {id: topProjectsData[2].product}, attributes: ['name'], raw: true, paranoid: false}), ventas: topProjectsData[2].Count} ] let topRankedProjectsData = await this.topRankedProject() @@ -134,7 +134,7 @@ const DashboardService = { return { totalProjects: totalProjects.count, activeSubscriptions: 75, - totalSales: `$${sales.valorizado - devoluciones.valorizado}`, + totalSales: sales.contador - devoluciones.contador, totalRevenue: `$${sales.valorizado - devoluciones.valorizado + donaciones.valorizado}`, averageSalesPerUser: await this.averageSales(id,fecha), activeProjects: count, @@ -161,6 +161,45 @@ const DashboardService = { return error } }, + userSalesDetail: async function (userId,desde,hasta) { + try { + const ProjectsIds = await Projects.findAll({ + include: { model: Users, where: {id: userId}, attributes: [] }, + attributes: ['id']}); + let userProducts = [] + for (let i in ProjectsIds) { userProducts.push(ProjectsIds[i].id)} + let dates = [] + //const hasta = new Date(fecha.getFullYear(),fecha.getMonth(),fecha.getDate()+30,0,0,0) + while (desde <= hasta) { + dates.push(desde) + desde = addDays(desde,1) + } + let ventas = [] + for (let i in dates) { + const data = await Payments.findAll({ + where: { + product: userProducts, + createdAt: dates[i], + status: 'completed', + concept: 'venta' + }, + attributes: [ + ['createdAt', 'fecha'], + [Sequelize.fn('count',Sequelize.col('product')),'proyectosVendidos'], + [Sequelize.fn('sum', Sequelize.col('paymentAmount')), 'ganancias'] + ], + group: ['fecha'], + raw: true + }); + data.length !== 0? + ventas.push({...data[0], fecha: format(data[0].fecha, 'yyyy-MM-dd')}) + : ventas.push({fecha: format(dates[i], 'yyyy-MM-dd'), proyectosVendidos: 0, ganancias: 0}); + } + return ventas + } catch (error) { + return error + } + }, userDonations: async function (userId,fecha) { try { let donations = [] @@ -345,9 +384,10 @@ const DashboardService = { [Sequelize.fn('count',Sequelize.col('paymentAmount')), 'Count'] ], //group: ['product'], - where: {createdAt: { - [Op.between]: [fecha, hasta] - }} + where: { + createdAt: {[Op.between]: [fecha, hasta]}, + status: 'completed' + } }) return data } catch (error) { @@ -427,4 +467,4 @@ const DashboardService = { } } -module.exports = DashboardService \ No newline at end of file +module.exports = DashboardService diff --git a/server/src/utils/projectsUser.json b/server/src/utils/projectsUser.json index 5ff56638..6ee7b8cf 100644 --- a/server/src/utils/projectsUser.json +++ b/server/src/utils/projectsUser.json @@ -1,6 +1,10 @@ { "data": [ + { + "ProjectId": 5, + "UserId": 1 + }, { "ProjectId": 1, "UserId": 5 @@ -66,4 +70,4 @@ "UserId": 5 } ] - } \ No newline at end of file + } diff --git a/server/src/utils/users.json b/server/src/utils/users.json index 4ada1e3f..eddf5f65 100644 --- a/server/src/utils/users.json +++ b/server/src/utils/users.json @@ -17,7 +17,7 @@ }, { "name": "Alexis", - "email": "Alexis@projunity.com", + "email": "draco_alexis@hotmail.com", "password": "projunity", "image": "https://www.spotteron.net/images/icons/user60.png", "githubUser": "dracoalex84",