diff --git a/front/components/PROJUNITY.png b/front/components/PROJUNITY.png new file mode 100644 index 0000000..87ad172 Binary files /dev/null and b/front/components/PROJUNITY.png differ diff --git a/front/components/admin/HeadAndFooter.jsx b/front/components/admin/HeadAndFooter.jsx index 06606af..92f92e1 100644 --- a/front/components/admin/HeadAndFooter.jsx +++ b/front/components/admin/HeadAndFooter.jsx @@ -29,6 +29,10 @@ import { useRouter } from "next/router"; import { getSesion, logout } from "../../redux/actions/actionsUser"; import Swal from "sweetalert2"; import { useDispatch, useSelector } from "react-redux"; +import HomeIcon from "@mui/icons-material/Home"; +import { Flex } from "@chakra-ui/react"; + + // If loading a variable font, you don't need to specify the font weight const inter = Inter({ @@ -106,16 +110,19 @@ const HeadFooter = ({ children }) => { ProjUnity + + + + + Home + + + - - - Home - - diff --git a/front/components/admin/transactionDetalles.jsx b/front/components/admin/transactionDetalles.jsx index 7b7c145..d90641f 100644 --- a/front/components/admin/transactionDetalles.jsx +++ b/front/components/admin/transactionDetalles.jsx @@ -26,6 +26,10 @@ const TransactionDetailsPage = ({ transaction }) => { // Agregar contenido al PDF doc.text("Detalles de la Transacción", 10, 10); + + const imgData = "./components/PROJUNITY.png"; // Reemplaza con la URL de tu imagen + doc.addImage(imgData, "PNG", 10, 40, 50, 50); + doc.autoTable({ head: [["Campo", "Valor"]], body: [ @@ -33,16 +37,14 @@ const TransactionDetailsPage = ({ transaction }) => { ["Nombre", transaction.nombre], ["Fecha", transaction.fecha], ["Total", transaction.total], + ["Producto", transaction.metodoPago], ["Estado del Pago", transaction.estadoPago], - ["Método de Pago", transaction.metodoPago], ], }); // Descargar el PDF doc.save("DetallesTransaccion.pdf"); } - - return ( @@ -71,6 +73,10 @@ const TransactionDetailsPage = ({ transaction }) => { Total: {transaction.total} + + Producto: + {transaction.metodoPago} + Estado del Pago: @@ -87,10 +93,6 @@ const TransactionDetailsPage = ({ transaction }) => { - - Método de Pago: - {transaction.metodoPago} - @@ -104,4 +106,4 @@ const TransactionDetailsPage = ({ transaction }) => { ); }; -export default TransactionDetailsPage; +export default TransactionDetailsPage; \ No newline at end of file diff --git a/front/package.json b/front/package.json index b173a0f..b72b076 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/admin/detallesTransaccion.jsx b/front/pages/admin/detallesTransaccion.jsx index cc8f830..7b1f16b 100644 --- a/front/pages/admin/detallesTransaccion.jsx +++ b/front/pages/admin/detallesTransaccion.jsx @@ -5,28 +5,28 @@ import HeadFooter from "../../components/admin/HeadAndFooter"; const transactionsData = [ { - orderId: "12345", - nombre: "Juan Pérez", - fecha: "2023-10-15", - total: "$50.00", - estadoPago: "Aprobado", - metodoPago: "Tarjeta de Crédito", + orderId: "560", + nombre: "Dario", + fecha: "22023-10-27 06:42", + total: "$67.80", + metodoPago: "Laravel", + estadoPago: "Created", }, { - orderId: "54321", - nombre: "María González", - fecha: "2023-10-14", - total: "$30.00", - estadoPago: "Denegado", - metodoPago: "Transferencia Mercado Pago", + orderId: "559", + nombre: "Dario", + fecha: "2023-10-27 06:31", + total: "$15.00", + metodoPago: "WooCommerce", + estadoPago: "Created", }, { - orderId: "87435", - nombre: "Fernando Guevara", - fecha: "2023-08-16", - total: "$80.00", - estadoPago: "Pendiente", - metodoPago: "Mercado Pago", + orderId: "559", + nombre: "Dario", + fecha: "2023-10-27 06:31", + total: "$67.80", + metodoPago: "Laravel", + estadoPago: "Created", }, // Agrega más transacciones aquí ]; diff --git a/front/pages/admin/gananciasUser.jsx b/front/pages/admin/gananciasUser.jsx index 9cdc243..44e0573 100644 --- a/front/pages/admin/gananciasUser.jsx +++ b/front/pages/admin/gananciasUser.jsx @@ -50,19 +50,30 @@ const diasDeVenta = [ export default function GananciasView() { const dispatch = useDispatch(); - - const id = useSelector((state) => state.usersData.sesion.id); - console.log(id); - const userDashboardData = useSelector((state) => state.userDashboard.userDashboardData); - console.log(userDashboardData); - + React.useEffect(() => { + let sesion = JSON.parse(localStorage.getItem("sesion")); + if (sesion.id) { + dispatch(getUserDashboard(sesion.id)); + } + }, [dispatch]); - React.useEffect(() => { - dispatch(getUserDashboard(id)); - }, [dispatch, id]); + // const id = useSelector((state) => state.usersData.sesion.id); + // console.log(id); + // React.useEffect(() => { + // if (id) { + // dispatch(getUserDashboard(id)); + // } + // }, [dispatch, id]); + const userDashboardData = useSelector( + (state) => state.userDashboard.userDashboardData + ); + console.log(userDashboardData); + + const loading = useSelector((state) => state.userDashboard.loading); + if (loading) return ; // Preparar datos para el gráfico de barras const data = []; @@ -81,9 +92,7 @@ export default function GananciasView() { } return total; } - const loading = useSelector((state) => state.projectsData.loading); - //* Aqui se maneja el loader - if (loading) return ; + return ( - project.name.toLowerCase().includes(searchTerm.toLowerCase()) - ); + const dispatch = useDispatch(); + + React.useEffect(() => { + /* projects.length === 0 && */ dispatch(getProjects()); + // dispatch(getCategory()); + }, [dispatch]); + + const projects = useSelector((state) => state.projectsData.projectsFilter); + console.log(projects) + const [searchTerm, setSearchTerm ] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 5; -const loading = useSelector((state) => state.projectsData.loading); -//* Aqui se maneja el loader -if (loading) return ; + + const loading = useSelector((state) => state.projectsData.loading); + //* Aqui se maneja el loader + if (loading) return ; + + const filteredProjects = projects.filter((project) => + project.name.toLowerCase().includes(searchTerm.toLowerCase()) +); +// Cálculo del total de páginas para la paginación +const totalPages = Math.ceil(filteredProjects.length / itemsPerPage); + +// Función para obtener los proyectos de la página actual +const getCurrentPageProjects = () => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return filteredProjects.slice(startIndex, endIndex); +}; + return ( @@ -79,11 +104,20 @@ if (loading) return ; - {filteredProjects.map((project) => ( + {getCurrentPageProjects() + .filter((project) => + project.name.toLowerCase().includes(searchTerm.toLowerCase()) + ) + .map((project) => ( {project.id} {project.name} - {project.owner} + + {project.Users.map((user) => ( + {user.githubUser} + + ))} + {project.status} @@ -104,6 +138,27 @@ if (loading) return ; ))} + {/* Agregar la paginación */} + + + + ); diff --git a/front/pages/admin/gestionUsers.jsx b/front/pages/admin/gestionUsers.jsx index 55dbe58..448a088 100644 --- a/front/pages/admin/gestionUsers.jsx +++ b/front/pages/admin/gestionUsers.jsx @@ -17,73 +17,37 @@ import { import HeadFooter from "../../components/admin/HeadAndFooter"; import Swal from "sweetalert2"; import { useDispatch, useSelector } from "react-redux"; -import { getUsers, deleteUser, restoreUser } from "../../redux/actions/actionsDashboard"; +import { + getUsers, + deleteUser, + restoreUser, +} from "../../redux/actions/actionsDashboard"; import Loader from "../../components/layout/loader"; +import { getSesion } from "../../redux/actions/actionsUser"; -// const userData = [ -// { -// id: 1, -// name: "Juan Ponce", -// email: "usuario1@example.com", -// status: "Activo", -// reasonForBlock: "Comportamiento inapropiado", -// reasonForSuspension: "Incumplimiento de términos", -// lastActivities: [ -// "Inició sesión a las 10:30 AM", -// "Publicó un nuevo artículo a las 11:45 AM", -// "Realizó una compra a las 2:15 PM", -// ], -// }, -// { -// id: 2, -// name: "María López", -// email: "maria@example.com", -// status: "Bloqueado", -// reasonForBlock: "Violación de las políticas", -// reasonForSuspension: null, // Este usuario no está suspendido -// lastActivities: [ -// "Inició sesión a las 9:45 AM", -// "Actualizó su perfil a las 10:30 AM", -// "Envió un mensaje a las 12:15 PM", -// ], -// }, -// { -// id: 3, -// name: "Roberto Sánchez", -// email: "roberto@example.com", -// status: "Activo", -// reasonForBlock: null, // Este usuario no está bloqueado -// reasonForSuspension: null, // Este usuario no está suspendido -// lastActivities: [ -// "Inició sesión a las 8:00 AM", -// "Publicó una imagen a las 9:30 AM", -// "Comentó en un artículo a las 11:45 AM", -// ], -// }, -// { -// id: 4, -// name: "Damian Magri", -// email: "daminao@example.com", -// status: "Suspendido", -// reasonForBlock: null, // Este usuario no está bloqueado -// reasonForSuspension: "Inactividad por 30 dias", // Este usuario no está suspendido -// lastActivities: null, -// }, -// ]; - export default function GestionUsuarios() { - const dispatch = useDispatch(); + const dispatch = useDispatch(); + + React.useEffect(() => { - dispatch(getUsers()) + let sesion = JSON.parse(localStorage.getItem("sesion")); + console.log(sesion) + if (sesion.id) { + console.log(sesion.id) + dispatch(getUsers()); + // dispatch(getSesion()); + } }, [dispatch]); -const userData = useSelector((state) => state.userDashboard.dataUsers) + const userData = useSelector((state) => state.userDashboard.dataUsers); + console.log(userData); + + const loading = useSelector((state) => state.userDashboard.loading); + if (loading) return ; -const loading = useSelector((state) => state.projectsData.loading); -//* Aqui se maneja el loader -if (loading) return ; + // const sesionId = useSelector((state) => state.usersData.sesion) const blockUser = (userId) => { // Mostrar una alerta de SweetAlert2 para confirmar el bloqueo del usuario @@ -96,14 +60,21 @@ if (loading) return ; cancelButtonText: "Cancelar", }).then((result) => { if (result.isConfirmed) { + // if (typeof sesionId?.access === false) { + // Swal.fire({ + // icon: "warning", + // title: "Inicia sesión para seguir con la compra", + // footer: 'Por que no te loggeas primero?', + // }); + // } else { dispatch(deleteUser(userId)); Swal.fire("Bloqueado", "El usuario ha sido bloqueado.", "success"); + // } } }); }; - const restoreUser = (userId) => { - + const restoreUsers = (userId) => { Swal.fire({ title: "Desbloquear usuario", text: "¿Estás seguro de que deseas desbloquear a este usuario?", @@ -114,11 +85,14 @@ if (loading) return ; }).then((result) => { if (result.isConfirmed) { dispatch(restoreUser(userId)); - Swal.fire("Desbloqueado", "El usuario ha sido desbloqueado.", "success"); + Swal.fire( + "Desbloqueado", + "El usuario ha sido desbloqueado.", + "success" + ); } }); }; - return ( @@ -140,36 +114,38 @@ if (loading) return ; ID Nombre - Correo Electrónico + Correo Electrónico Estado Acciones - {userData.map((user) => ( - + {userData?.map((user) => ( + {user.id} {user.name} {user.email} - {user.status} + {user.isBlocked ? "Bloqueado" : "Activo"} - {/* - - */} diff --git a/front/pages/admin/historialUser.jsx b/front/pages/admin/historialUser.jsx index f432100..f675ae7 100644 --- a/front/pages/admin/historialUser.jsx +++ b/front/pages/admin/historialUser.jsx @@ -68,7 +68,7 @@ export default function HistorialUsuarios() { const [pdf, setPdf] = useState(null); const [searchUser, setSearchUser] = useState(""); const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 5; + const itemsPerPage = 6; // Función para filtrar el historial por usuario const filteredByUser = userHistoryData.filter((historyItem) => diff --git a/front/pages/admin/index.js b/front/pages/admin/index.js index 6edfa4e..85363b5 100644 --- a/front/pages/admin/index.js +++ b/front/pages/admin/index.js @@ -96,7 +96,7 @@ const AdminDashboard = () => { Dashboard de Administrador {/* Contenedor Flex para la tarjeta y el resumen */} - + {/* Tarjeta del Usuario */} {/* Tarjetas de los Estadisticos */} diff --git a/front/pages/admin/reportesUser.jsx b/front/pages/admin/reportesUser.jsx index e9c831f..0e111e5 100644 --- a/front/pages/admin/reportesUser.jsx +++ b/front/pages/admin/reportesUser.jsx @@ -19,6 +19,10 @@ import "jspdf-autotable"; import { useDispatch, useSelector } from "react-redux"; import { getProjects } from "../../redux/actions/actions"; import Swal from "sweetalert2"; +import { getUsers } from "../../redux/actions/actionsDashboard"; +import Loader from "../../components/layout/loader"; + + const userReportsData = [ { @@ -80,9 +84,15 @@ export default function ReportesUsuarios() { React.useEffect(() => { dispatch(getProjects()); }, [dispatch]); + + const [pdf, setPdf] = useState(null); + const loading = useSelector((state) => state.userDashboard.loading); + if (loading) return ; + + const generatePDF = () => { const doc = new jsPDF(); doc.text("Reportes de Usuarios", 10, 10); @@ -178,7 +188,7 @@ export default function ReportesUsuarios() { {reportItem.userReported} {reportItem.reason} - + {reportItem.project} diff --git a/front/redux/actions/actionsDashboard.js b/front/redux/actions/actionsDashboard.js index 1ba9de8..a41de3f 100644 --- a/front/redux/actions/actionsDashboard.js +++ b/front/redux/actions/actionsDashboard.js @@ -33,32 +33,33 @@ export const getUsers = () => { export const deleteUser = (userId) => { return async (dispatch) => { - try { - const { data } = await axios.delete(`${ENDPOINT}users/${userId}`); - dispatch({ - type: DELETE_USER, - payload: data - }); - } catch (err) { - console.log(err); - } - } - -} - -export const restoreUser = (userId) => { + try { + const { data } = await axios.delete(`${ENDPOINT}users/${userId}`); + console.log(userId) + dispatch({ + type: DELETE_USER, + payload: data, + }); + } catch (err) { + console.log(err); + } + }; + } + + export const restoreUser = (userId) => { return async (dispatch) => { - try { - const { data } = await axios.put(`${ENDPOINT}users/restore/${userId}`); - dispatch({ - type: RESTORE_USER, - payload: data - }); - } catch (err) { - console.log(err); - } - } -} + try { + const { data } = await axios.put(`${ENDPOINT}users/restore/${userId}`); + dispatch({ + type: RESTORE_USER, + payload: data, + }); + } catch (err) { + console.log(err); + } + }; + } + export const paymentRecord = () => { return async (dispatch) => { diff --git a/front/redux/reducers/dashboardReducer.js b/front/redux/reducers/dashboardReducer.js index 9932b40..8836385 100644 --- a/front/redux/reducers/dashboardReducer.js +++ b/front/redux/reducers/dashboardReducer.js @@ -1,48 +1,58 @@ -import { DELETE_USER, GET_USERS, RESTORE_USER, USER_DASHBOARD, PAYMENT_RECORD } from "../types"; +import { + DELETE_USER, + GET_USERS, + RESTORE_USER, + USER_DASHBOARD, + PAYMENT_RECORD, +} from "../types"; const initialState = { - userDashboardData: {}, - dataUsers: [], - payments: [], - loading: true, + userDashboardData: {}, + dataUsers: [], + deletedUsers: [], + payments: [], + loading: true, }; const userDashboardReducer = (state = initialState, action) => { -/* console.log(action.payload) */ - switch (action.type) { - case USER_DASHBOARD: - return { - ...state, - userDashboardData: action.payload, - loading: false, - }; - case GET_USERS: - return { - ...state, - dataUsers: action.payload, - loading: false, - } - case DELETE_USER: - return { - ...state, - dataUsers: action.payload, - loading: false, - } - case RESTORE_USER: - return { - ...state, - dataUsers: action.payload, - loading: false, - } - case PAYMENT_RECORD: - return { - ...state, - payments: action.payload, - loading: false, - } - default: - return state; - } + /* console.log(action.payload) */ + switch (action.type) { + case USER_DASHBOARD: + return { + ...state, + userDashboardData: action.payload, + loading: false, + }; + case GET_USERS: + return { + ...state, + dataUsers: action.payload, + loading: false, + }; + case DELETE_USER: + console.log(action.payload); + return { + ...state, + dataUsers: action.payload, + loading: false, + }; + + case RESTORE_USER: + return { + ...state, + deletedUsers: action.payload, + loading: false, + }; + + case PAYMENT_RECORD: + return { + ...state, + payments: action.payload, + loading: false, + }; + default: + return state; + } }; -export default userDashboardReducer; \ No newline at end of file +export default userDashboardReducer; diff --git a/front/redux/types.js b/front/redux/types.js index 8bc0698..1cb5122 100644 --- a/front/redux/types.js +++ b/front/redux/types.js @@ -43,5 +43,5 @@ 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 = "https://projunity-production.up.railway.app/"; diff --git a/server/index.js b/server/index.js index b2d4470..c5c0d22 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 08ee94e..6d1c5b6 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 c0ebe33..44bec01 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 e95756b..bf1b9a9 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 7ce4c1c..2eed793 100644 --- a/server/src/controllers/form.js +++ b/server/src/controllers/form.js @@ -1,4 +1,8 @@ const { ProjectServices } = require("../services"); +const {sendEmail} = require("./mailer"); +const { Users } = require('../db'); + + const formControllers = { createNewProject: async function (req, res) { @@ -7,14 +11,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 0000000..bdbdfbc --- /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 2bfd658..918a3b6 100644 --- a/server/src/controllers/mercadopago.js +++ b/server/src/controllers/mercadopago.js @@ -7,67 +7,50 @@ const { projects } = require("../utils/index.js"); // 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", @@ -83,67 +66,71 @@ console.log(items); 0 ); - console.log(items); - - const orderDb = await Payments.create({ - paymentId: global.id, - status: "created", - orderNumber: orderNumber[0].max + 1, - buyer: 1, - projects: 1, - paymentAmount: totalPrecio, - }); + 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 + } + ] + } - // console.log(orderDb) - res.json({ - id: global.id, - init_point: response.body.init_point, - orderDb, - }); - } catch (error) { - console.log(error); - } + res.json({id: global.id, init_point: response.body.init_point, itemsDb}) + + } catch (error) { + console.log(error); + } }, - // 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 - // }); - - 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 8ddc612..ec26703 100644 --- a/server/src/controllers/projects.js +++ b/server/src/controllers/projects.js @@ -52,6 +52,15 @@ const projectControllers = { } }, + getDeletedProjects: async function (req, res) { + try { + const deletedProjects = await Services.getDeletedProjects(); + res.status(200).json(deletedProjects); + } catch (error) { + res.status(500).json(error.message); + } +}, + restoreProject: async function (req, res) { try { const projectId = req.params.id; @@ -63,4 +72,4 @@ const projectControllers = { }, }; -module.exports = projectControllers; +module.exports = projectControllers; \ No newline at end of file diff --git a/server/src/controllers/users.js b/server/src/controllers/users.js index c940df9..83cf00e 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 b9ea458..51dc766 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 41b870c..d7b0c6f 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 a3bba29..ba3a1d5 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 aca2724..ae5fe2b 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 f447744..4b85a9f 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; - } - }, + }, + createUser: async function (userData) { + try { + const { name, email, password, image, twitterUser, emailUser, githubUser, linkedinUser, role} = userData - 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; - } + if ( !name || !email || !password /* || !image || !twitterUser || !emailUser || !githubUser <<== MODIFIQUE ESTO PARA PODER CREAR USUARIOS */ || !role) { - // save the changes - await user.save(); + 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 + } + }, - // 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); - } - }, + 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 656d1d5..d2dabf6 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']}) + + 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 843e0ab..989e544 100644 --- a/server/src/services/projects.js +++ b/server/src/services/projects.js @@ -14,7 +14,7 @@ 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 ? (condition = { @@ -62,84 +62,83 @@ const ProjectServices = { }) : null; rating - ? (condition = { - ...condition, - ratings: { - score: { - [Op.gte]: rating, - }, - // [Op.or]: [{ score: { [Op.eq]: score } }], + ? (condition = { + ...condition, + rating: { + score:{ + [Op.or]:{ + [Op.lt]: rating, + [Op.eq]: rating , + } }, - }) - : 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; - } 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; - } + 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: Ratings, + attributes: ["score", "comment"], + /* where: condition.rating, */ + through: { attributes: [] }, + }, + { + model: Users, + attributes: ['id','name','email','githubUser','twitterUser','linkedinUser'], + where: condition.users, + through: {attributes: []} + } + ], + where: condition.project, + paranoid: deleted? false : true + }); + return projectsFilter; } 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, @@ -179,6 +178,7 @@ const ProjectServices = { return error; } }, + createProjects: async function (projectData) { try { const { @@ -315,19 +315,61 @@ 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); } diff --git a/server/src/services/userDashboard.js b/server/src/services/userDashboard.js index 1991afb..f7654df 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) { @@ -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 5ff5663..6ee7b8c 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 4ada1e3..eddf5f6 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",