diff --git a/cosiap_api/dynamic_formats/urls.py b/cosiap_api/dynamic_formats/urls.py index 3c9d4dfd9fcf74949d611a0e6b202ecfb498163c..c5040dd50c3287618021f3b0f83067215360de56 100644 --- a/cosiap_api/dynamic_formats/urls.py +++ b/cosiap_api/dynamic_formats/urls.py @@ -10,4 +10,5 @@ urlpatterns = [ path('/', views.FormatoAPIView.as_view(), name='formatos_pk'), path('download//', views.DescargarFormatoView.as_view(), name='descargar_formato'), path('descargar-formato/', views.descargar_formato, name='descargar_formato_admin'), + path('manual/', views.descarga_manual, name='descargar_manual'), ] \ No newline at end of file diff --git a/cosiap_api/dynamic_formats/views.py b/cosiap_api/dynamic_formats/views.py index 8d5b1c7db55d232138c7f4cfdd0b9618f65280f0..5f45ef1bd0b77a0c31680e1e1b54b9e89efc745c 100644 --- a/cosiap_api/dynamic_formats/views.py +++ b/cosiap_api/dynamic_formats/views.py @@ -8,12 +8,13 @@ from rest_framework.generics import get_object_or_404 from .models import DynamicFormat from users.models import Solicitante from .serializer import DynamicFormatSerializer -from django.http import HttpResponse +from django.http import HttpResponse,FileResponse, Http404 from io import BytesIO from docx import Document import re from notificaciones.mensajes import Mensaje import logging +import os class FormatoConvenio(BasePermissionAPIView): @@ -240,4 +241,15 @@ def descargar_formato(request, pk): response['Content-Disposition'] = f'attachment; filename="{formato.nombre}"' return response except DynamicFormat.DoesNotExist: - raise Http404("El formato solicitado no existe.") \ No newline at end of file + raise Http404("El formato solicitado no existe.") + + +def descarga_manual(request): + ''' Descarga del manual de usuario ''' + file_path = os.path.join('static/manual/', 'Manual.pdf') + try: + response = FileResponse(open(file_path, 'rb'), content_type='application/pdf') + response['Access-Control-Allow-Origin'] = '*' + return response + except FileNotFoundError: + raise Http404("El archivo no existe") \ No newline at end of file diff --git a/cosiap_api/static/manual/Manual.pdf b/cosiap_api/static/manual/Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..74067b6efd91579f760696510405271fb4bdaeb7 Binary files /dev/null and b/cosiap_api/static/manual/Manual.pdf differ diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx index ddd0e101fe9656381cd5e1c08a6f983e919607d2..21b985b31752f0bdd458322aead37d9e432da292 100644 --- a/cosiap_frontend/src/App.jsx +++ b/cosiap_frontend/src/App.jsx @@ -41,6 +41,7 @@ import ListaFormatos from "./components/plantillas/ListaFormatos"; import CrearFormato from "./components/plantillas/CrearPlantilla"; import useColor from "./components/useColor"; import EditarColorLogo from "./components/design/colorLogo"; +import DescargarManual from "./components/plantillas/manual"; function App() { const [viewPageLoader, setViewPageLoader] = useState(false); @@ -127,7 +128,7 @@ function RoutesApp({ setViewPageLoader }) { }> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js index 5f516fb6ff18656d89d4bb27bc8a7bb83861faa3..4c15e940b5895a86da71da6b098bfb1f2ec2eb08 100644 --- a/cosiap_frontend/src/api.js +++ b/cosiap_frontend/src/api.js @@ -191,6 +191,7 @@ const api = { updateMinuta: (data) => ax.put('api/plantillas/minuta/',data), getConvenio: () => ax.get('api/plantillas/convenio'), updateConvenio: (data) => ax.put('api/plantillas/convenio/',data), + manual: (params) => ax.get('api/plantillas/manual/', params) }, convocatoria: { get: () => ax.get('api/administracion/convocatoria/'), diff --git a/cosiap_frontend/src/components/admin/TablaAdministradores.jsx b/cosiap_frontend/src/components/admin/TablaAdministradores.jsx index 4e96b67cea73aa9bb1233b42e58397ecfa665c54..b7dc456e06e0548b642705c4311b336b1401e216 100644 --- a/cosiap_frontend/src/components/admin/TablaAdministradores.jsx +++ b/cosiap_frontend/src/components/admin/TablaAdministradores.jsx @@ -130,33 +130,35 @@ const ListaAdmins = () =>{ const renderCell = (fila, key) => { const isEditing = editRow === fila.pk; - return isEditing ? ( - handleChange(e, fila.pk, key)} - ref={(el) => (inputRefs.current[fila.pk + key] = el)} - style={{ - fontSize: "14px", - padding: "2px 4px", - borderRadius: "4px", - height: "1.6em", - lineHeight: "1", - border: "1px solid #d3d3d3", - display: "inline-block", - width: "auto", - maxWidth: "100px", - }} - autoFocus - /> - ) : ( - handleSingleClick(e,fila)} + return ( +
handleSingleClick(e, fila)} onDoubleClick={() => handleEditRow(fila.pk)} - style={{ cursor: "pointer" }} + style={{ cursor: "pointer", display: "flex", alignItems: "center" }} > - {fila[key]} - + {isEditing ? ( + handleChange(e, fila.pk, key)} + ref={(el) => (inputRefs.current[fila.pk + key] = el)} + style={{ + fontSize: "14px", + padding: "2px 4px", + borderRadius: "4px", + height: "1.6em", + lineHeight: "1", + border: "1px solid #d3d3d3", + display: "inline-block", + width: "auto", + maxWidth: "100px", + }} + autoFocus + /> + ) : ( + {fila[key]} + )} +
); }; diff --git a/cosiap_frontend/src/components/admin/TablaUsuarios.jsx b/cosiap_frontend/src/components/admin/TablaUsuarios.jsx index 18f7e22ea7fd4de2eac9b5790054e5141129bbb8..85b7b8c11eed4bb30e98d504daa9e3500bcd6c80 100644 --- a/cosiap_frontend/src/components/admin/TablaUsuarios.jsx +++ b/cosiap_frontend/src/components/admin/TablaUsuarios.jsx @@ -4,7 +4,7 @@ import Tabla from "../common/utility/ReusableTable"; import MainContainer from "../common/utility/MainContainer"; import '@/App.css'; import { useNavigate } from "react-router-dom"; -import SearchInput from '@/components/common/utility/SearchInput' +import SearchInput from '@/components/common/utility/SearchInput'; import UserIcon from "@/components/common/utility/UserIcon"; const ListaUsuarios = () => { @@ -37,7 +37,7 @@ const ListaUsuarios = () => { fetchUsuarios(); }, []); - const handleSingleClick = (e,fila) => { + const handleSingleClick = (e, fila) => { e.preventDefault(); setSelectedRow(fila.id); // Muestra el menú para el registro seleccionado }; @@ -53,7 +53,6 @@ const ListaUsuarios = () => { }; const handleNavigateToHistorial = (id) => { - console.log(id); navigate(`/historial-solicitante/${id}`); }; @@ -125,8 +124,10 @@ const ListaUsuarios = () => { const columnas = [ { label: "", - render: (fila) => , - }, + render: (fila) => ( + + ), + }, { label: "CURP", render: (fila) => renderCell(fila, "curp") }, { label: "Nombre", render: (fila) => renderCell(fila, "nombre") }, { label: "Apellido Paterno", render: (fila) => renderCell(fila, "ap_paterno") }, @@ -137,33 +138,35 @@ const ListaUsuarios = () => { const renderCell = (fila, key) => { const isEditing = editRow === fila.id; - return isEditing ? ( - handleChange(e, fila.id, key)} - ref={(el) => (inputRefs.current[fila.id + key] = el)} - style={{ - fontSize: "14px", - padding: "2px 4px", - borderRadius: "4px", - height: "1.6em", - lineHeight: "1", - border: "1px solid #d3d3d3", - display: "inline-block", - width: "auto", - maxWidth: "100px", - }} - autoFocus - /> - ) : ( - handleSingleClick(e,fila)} + return ( +
handleSingleClick(e, fila)} onDoubleClick={() => handleEditRow(fila.id)} - style={{ cursor: "pointer" }} + style={{ cursor: "pointer", display: "flex", alignItems: "center" }} > - {fila[key]} - + {isEditing ? ( + handleChange(e, fila.id, key)} + ref={(el) => (inputRefs.current[fila.id + key] = el)} + style={{ + fontSize: "14px", + padding: "2px 4px", + borderRadius: "4px", + height: "1.6em", + lineHeight: "1", + border: "1px solid #d3d3d3", + display: "inline-block", + width: "auto", + maxWidth: "100px", + }} + autoFocus + /> + ) : ( + {fila[key]} + )} +
); }; @@ -172,7 +175,7 @@ const ListaUsuarios = () => { setIsSuccess(isSuccess); setTimeout(() => { - setAlertMessage(''); + setAlertMessage(''); }, 3000); }; @@ -190,9 +193,8 @@ const ListaUsuarios = () => { }; const handleUpdate = async (id) => { - console.log(registerChange[id]); try { - await api.usuarios.solicitantes.update(id, registerChange[id]) + await api.usuarios.solicitantes.update(id, registerChange[id]); const response = await api.usuarios.solicitantes.get(); setUsuarios(response.data.data); } catch (error) { @@ -213,17 +215,16 @@ const ListaUsuarios = () => { )}
- - + +
- + {selectedRow && !isConfirmingDelete && (
)} - {/* Tarjeta emergente */} - {showCard && ( +
+ {showCard && (
- -

Información del Usuario

-

Nombre: {nombreActual}

+

Detalles del Usuario

CURP: {curpActual}

+

Nombre: {nombreActual}

Email: {emailActual}

+
- )} - + )} ); }; export default ListaUsuarios; - diff --git a/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx b/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx index 5b59129c9579625b4df5b995aef5dd1e71c12f23..37ee3d5c9df9d9726eb7d4f3eff777a849d70886 100644 --- a/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx +++ b/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx @@ -1,4 +1,5 @@ import '@/App.css'; +import { apiUrl } from '@/api'; export const renderElemento = (seccionId, elemento, handleInputChange, handleCheckboxChange, respuestas) => { const { tipo, opciones, id, nombre, obligatorio } = elemento; @@ -153,7 +154,7 @@ export const renderElemento = (seccionId, elemento, handleInputChange, handleChe /> {valorPrellenado && ( { const { tipo, opciones, id, nombre, obligatorio } = elemento; @@ -99,7 +100,7 @@ export const renderElemento = (seccionId, elemento, handleInputChange, handleChe
{valorPrellenado && ( { + const navigate = useNavigate(); + + useEffect(() => { + const redireccion = async () => { + await handleDownload(); // Esperar a que se complete la descarga antes de navegar + navigate('/inicio'); + }; + redireccion(); + }, []); // Array de dependencias vacío para que `useEffect` se ejecute solo una vez + + const handleDownload = async () => { + try{ + const response = await api.formatos.manual( { + responseType: 'blob', + }); + // Crear un objeto URL para descargar el archivo + const url = window.URL.createObjectURL(new Blob([response.data])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', 'Manual.pdf'); + document.body.appendChild(link); + link.click(); + + // Limpiar el objeto URL + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + } + catch (error){ + console.error('Error al descargar el archivo:', error); + } + + }; + + return null; // Este componente no renderiza nada en la UI +}; + +export default DescargarManual;