From 582796b6eb926f826c70403607eccdebe8d7e268 Mon Sep 17 00:00:00 2001 From: Luis Matamoros <47126608+RyuDSora@users.noreply.github.com> Date: Thu, 22 Aug 2024 22:33:02 -0600 Subject: [PATCH] cambios --- client/src/components/Articles.jsx | 23 ++- .../DashboardStudent/ArticlesST.jsx | 12 +- .../components/DashboardStudent/DashST.jsx | 2 +- .../components/DashboardTutor/ArticlesTU.jsx | 48 ++++- client/src/components/NavBarT.jsx | 2 +- client/src/components/TopSellingCoursesST.jsx | 191 ++++++++++++++++++ client/src/components/Urls.jsx | 4 +- 7 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 client/src/components/TopSellingCoursesST.jsx diff --git a/client/src/components/Articles.jsx b/client/src/components/Articles.jsx index e622b57..60b5e82 100644 --- a/client/src/components/Articles.jsx +++ b/client/src/components/Articles.jsx @@ -1,9 +1,11 @@ import { useEffect, useState } from 'react'; import { Row, Col, Card, Modal, Button } from 'react-bootstrap'; -import { url } from './Urls'; +import { url,uritutor } from './Urls'; import axios from 'axios'; import Cookies from 'js-cookie'; import { decryptValue, encryptionKey } from './hashes'; +import { toast, ToastContainer } from 'react-toastify'; + const Articles = () => { const [articles, setArticles] = useState([]); @@ -18,8 +20,21 @@ const Articles = () => { const session = Cookies.get('$3s1.4') ? decryptValue(Cookies.get('$3s1.4'), encryptionKey) : null; if (session) { const id = decryptValue(Cookies.get('#gt156'), encryptionKey); // Obtiene el ID del profesor + const leerTutor = async(ID) =>{ + try { + const response = await axios.get(`${uritutor}/${ID}`); + const idTutor = response.data.id; + setTeacherId(idTutor) + } catch (error) { + + } + } + const role = decryptValue(Cookies.get('&0l3'), encryptionKey); // Obtiene el rol del usuario - setTeacherId(id); + if (userRole === 'tutor') { + leerTutor(id) + } + setUserRole(role); // Establece el rol del usuario setIsLoggedIn(true); // Si hay sesión, marcamos que el usuario está logueado } @@ -30,6 +45,7 @@ const Articles = () => { const fetchArticles = async () => { try { let response; + if (userRole === 'tutor' && teacherId) { // Obtener todos los artículos del tutor response = await axios.get(`${url}/articles/teacher/${teacherId}`); @@ -43,7 +59,7 @@ const Articles = () => { // Si el usuario es tutor, ordenamos por comentarios de mayor a menor if (userRole === 'tutor') { - articlesData.sort((a, b) => b.comments.length - a.comments.length); // Suponiendo que 'comments' es un array en cada artículo + //articlesData.sort((a, b) => b.comments.length - a.comments.length); // Suponiendo que 'comments' es un array en cada artículo } setArticles(articlesData); @@ -69,6 +85,7 @@ const Articles = () => { return ( <> + {isLoggedIn ? ( <> diff --git a/client/src/components/DashboardStudent/ArticlesST.jsx b/client/src/components/DashboardStudent/ArticlesST.jsx index e24da90..aaca8a0 100644 --- a/client/src/components/DashboardStudent/ArticlesST.jsx +++ b/client/src/components/DashboardStudent/ArticlesST.jsx @@ -4,6 +4,7 @@ import Cookies from 'js-cookie'; import { decryptValue, encryptionKey } from '../hashes'; import { url } from '../Urls'; import axios from 'axios'; +import { toast, ToastContainer } from 'react-toastify'; const ArticlesST = () => { const [articles, setArticles] = useState([]); @@ -73,11 +74,17 @@ const ArticlesST = () => { }); // Actualiza artículos para incluir el nuevo comentario + setSelectedArticle(prev => ({ + ...prev, + comments: [...(prev.comments || []), response.data] + }) ); + setArticles(prevArticles => prevArticles.map(article => article.id === articleId ? { ...article, comments: [...(article.comments || []), response.data] } : article ) ); + toast.success('se agregó su comentario') setNewComment(""); // Resetea el comentario } catch (error) { console.error('Error adding comment:', error); @@ -87,7 +94,8 @@ const ArticlesST = () => { return ( -

Libros

+ +
Tus Artículos
{loading ? ( ) : error ? ( @@ -145,7 +153,7 @@ const ArticlesST = () => { required /> - + ) : (

Debes estar autenticado para agregar comentarios.

diff --git a/client/src/components/DashboardStudent/DashST.jsx b/client/src/components/DashboardStudent/DashST.jsx index e8595b6..0c20a97 100644 --- a/client/src/components/DashboardStudent/DashST.jsx +++ b/client/src/components/DashboardStudent/DashST.jsx @@ -1,5 +1,5 @@ -import TopSellingCourses from '../TopSellingCourses'; +import TopSellingCourses from '../TopSellingCoursesST'; import Articles from "../Articles"; import NextLessons from '../NextLessonsST'; diff --git a/client/src/components/DashboardTutor/ArticlesTU.jsx b/client/src/components/DashboardTutor/ArticlesTU.jsx index 3bc5a63..94b8173 100644 --- a/client/src/components/DashboardTutor/ArticlesTU.jsx +++ b/client/src/components/DashboardTutor/ArticlesTU.jsx @@ -2,8 +2,11 @@ import { useEffect, useState, useCallback } from 'react'; import { Row, Col, Card, Modal, Button, Form } from 'react-bootstrap'; import axios from 'axios'; import Cookies from 'js-cookie'; +import { confirmAlert } from 'react-confirm-alert'; import { decryptValue, encryptionKey } from '../hashes'; -import { url } from '../Urls'; // Asegúrate de que la URL está configurada correctamente +import { url, uritutor } from '../Urls'; // Asegúrate de que la URL está configurada correctamente +import { toast, ToastContainer } from 'react-toastify'; +import { FaTrash, FaEdit } from 'react-icons/fa'; const ArticlesTU = () => { const [articles, setArticles] = useState([]); @@ -21,8 +24,18 @@ const ArticlesTU = () => { if (session) { const role = decryptValue(Cookies.get('&0l3'), encryptionKey); const id = decryptValue(Cookies.get('#gt156'), encryptionKey); // Esto obtiene el ID del usuario + const leerTutor = async(ID) =>{ + try { + const response = await axios.get(`${uritutor}/${ID}`); + const idTutor = response.data.id; + setTeacherId(idTutor) + } catch (error) { + + } + } + leerTutor(id); + setUserRole(role); - setTeacherId(id); } }, []); @@ -66,31 +79,52 @@ const ArticlesTU = () => { try { if (isEditing) { await axios.put(`${url}/articles/${selectedArticle.id}`, { ...formData }); + toast.success('Se ha modificado el artículo exitosamente') } else { await axios.post(`${url}/articles`, { ...formData, teacher_id: teacherId }); + toast.success('Se ha creado el artículo exitosamente') } fetchArticles(); handleCloseModal(); } catch (error) { console.error('Error saving article:', error); + toast.error('No se puede crear el artículo') } }; const handleDelete = async (id) => { try { await axios.delete(`${url}/articles/${id}`); + toast.warning('Se eliminó el artículo exitosamente') fetchArticles(); } catch (error) { console.error('Error deleting article:', error); } }; - + const confirmDelete = (id) => { + confirmAlert({ + title: 'Confirmar eliminación', + message: '¿Estás seguro de que deseas eliminar el Artículo?', + buttons: [ + { + label: 'Sí', + onClick: () => { handleDelete(id) }, + }, + { + label: 'No', + onClick: () => { }, + }, + ], + }); + }; return ( <> + +
Tus Artículos
{userRole && ( // Comprobación del rol de usuario
-

Gestión de Artículos

- @@ -101,8 +135,8 @@ const ArticlesTU = () => { {article.title} {new Date(article.created_at).toLocaleDateString()} - - + + diff --git a/client/src/components/NavBarT.jsx b/client/src/components/NavBarT.jsx index 35c6d6f..9decdf1 100644 --- a/client/src/components/NavBarT.jsx +++ b/client/src/components/NavBarT.jsx @@ -84,7 +84,7 @@ function NavBarT({ isLoggedIn }) { ):( <>{Tutor ? (<> {/**aqui van las opciones del tutor */} - {lista("/dashboardtutor/dash",,'Mi Panel')} + {lista("/dashboardtutor/dash-tu",,'Mi Panel')} {lista("/dashboardtutor/my-courses",,'Mis Cursos')} {lista("/dashboardtutor/my-students",,'Mis Estudiantes')} {lista("/dashboardtutor/chats",,'Chats')} diff --git a/client/src/components/TopSellingCoursesST.jsx b/client/src/components/TopSellingCoursesST.jsx new file mode 100644 index 0000000..5de0b3e --- /dev/null +++ b/client/src/components/TopSellingCoursesST.jsx @@ -0,0 +1,191 @@ +import React, { useState } from 'react'; +import { Card, Table, Button, Modal, Alert } from 'react-bootstrap'; +import { Doughnut } from 'react-chartjs-2'; +import { + Chart as ChartJS, + ArcElement, + Tooltip, + Legend, +} from 'chart.js'; + +ChartJS.register( + ArcElement, + Tooltip, + Legend +); + +const generateRandomData = () => { + const courses = []; + const enrollments = []; + const prices = []; + const backgroundColors = []; + + const colors = [ + 'rgba(255, 99, 132, 0.6)', + 'rgba(54, 162, 235, 0.6)', + 'rgba(255, 206, 86, 0.6)', + 'rgba(75, 192, 192, 0.6)', + 'rgba(153, 102, 255, 0.6)', + 'rgba(255, 159, 64, 0.6)' + ]; + + + + for (let i = 1; i <= 4; i++) { + backgroundColors.push(colors[i % colors.length]); + } + + return { courses, enrollments, prices, backgroundColors }; +}; + +const { courses, enrollments, prices, backgroundColors } = generateRandomData(); + +const topCourses = courses + .map((course, index) => ({ + course, + enrollment: enrollments[index], + price: prices[index], + backgroundColor: backgroundColors[index] + })) + .sort((a, b) => b.enrollment - a.enrollment) + .slice(0, 3); + +const data = { + labels: topCourses.map(c => c.course), + datasets: [ + { + label: 'Número de suscriptores', + data: topCourses.map(c => c.enrollment), + backgroundColor: topCourses.map(c => c.backgroundColor), + borderColor: 'rgba(255, 255, 255, 1)', + borderWidth: 1, + }, + ], +}; + +const options = { + responsive: true, + plugins: { + legend: { + display: false, + }, + tooltip: { + callbacks: { + label: (context) => `${context.label}: ${context.raw} suscriptores` + } + }, + title: { + display: false, + text: 'Cursos más vendidos', + }, + }, + cutout: '0%', +}; + +const TopSellingCourses = () => { + const [show, setShow] = useState(false); + + const totalEnrollments = enrollments.reduce((a, b) => a + b, 0); + const totalRevenue = enrollments.reduce((sum, enrollment, index) => sum + enrollment * prices[index], 0).toFixed(2); + + const handleClose = () => setShow(false); + const handleShow = () => setShow(true); + + return ( + <> + + + {courses.length === 0 ? ( + + No hay datos disponibles por el momento. + + ) : ( +
+
+
+ +
+ Total
Lps {totalRevenue} +
+
+
+
+ Cursos más vendidos +
+ + + + + + + + + + {topCourses.map((course, index) => ( + + + + + + ))} + +
CursoSuscrip.Precio
{course.course}{course.enrollment}Lps {course.price.toFixed(2)}
+
+
+ +
+
+
+ )} +
+
+ + + + Todos los Cursos + + + {courses.length === 0 ? ( + + No hay cursos disponibles. + + ) : ( + + + + + + + + + + + {courses.map((course, index) => ( + + + + + + + ))} + + + + + + + +
CursoSuscrip.PrecioIngreso
{course}{enrollments[index]}Lps {prices[index].toFixed(2)}Lps {(enrollments[index] * prices[index]).toFixed(2)}
Total Ingresos:Lps {totalRevenue}
+ )} +
+ + + +
+ + ); +} + +export default TopSellingCourses; diff --git a/client/src/components/Urls.jsx b/client/src/components/Urls.jsx index ca3ec5f..ea33518 100644 --- a/client/src/components/Urls.jsx +++ b/client/src/components/Urls.jsx @@ -1,5 +1,5 @@ -export const url = 'http://localhost:3000'; //para hacer pruebas en local -//export const url = 'https://tutorias-five.vercel.app'; //servidor de vercel +//export const url = 'http://localhost:3000'; //para hacer pruebas en local +export const url = 'https://tutorias-five.vercel.app'; //servidor de vercel //urls para trabajar con tablas en general: Administrador