From dbea83ce97b09748bb60ebe5a71fb8a64bc1afe5 Mon Sep 17 00:00:00 2001
From: AdalbertoCV <34152734@uaz.edu.mx>
Date: Thu, 19 Sep 2024 13:13:41 -0600
Subject: [PATCH] Componente de crear modalidad implementado
---
cosiap_api/modalidades/serializers.py | 1 -
cosiap_frontend/src/App.jsx | 2 +
cosiap_frontend/src/api.js | 37 ++
.../components/modalidades/CrearModalidad.css | 160 ++++++
.../components/modalidades/CrearModalidad.jsx | 461 ++++++++++++++++++
.../modalidades/ListaModalidades.jsx | 16 +-
.../components/modalidades/Modalidades.jsx | 17 +
7 files changed, 692 insertions(+), 2 deletions(-)
create mode 100644 cosiap_frontend/src/components/modalidades/CrearModalidad.css
create mode 100644 cosiap_frontend/src/components/modalidades/CrearModalidad.jsx
diff --git a/cosiap_api/modalidades/serializers.py b/cosiap_api/modalidades/serializers.py
index 21428b7..a759fcd 100644
--- a/cosiap_api/modalidades/serializers.py
+++ b/cosiap_api/modalidades/serializers.py
@@ -2,7 +2,6 @@ from rest_framework import serializers
from modalidades.models import Modalidad
class ModalidadSerializer(serializers.ModelSerializer):
- monto = serializers.FloatField(write_only=True)
class Meta:
model = Modalidad
diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx
index 8745014..e20c2f0 100644
--- a/cosiap_frontend/src/App.jsx
+++ b/cosiap_frontend/src/App.jsx
@@ -24,6 +24,7 @@ import { useState } from "react";
import LayoutBaseAuthenticator from "@/components/common/layouts/LayoutBaseAuthenticator";
import LayoutBaseNavigation from "@/components/common/layouts/LayoutBaseNavigation";
import Modalidades from "@/components/modalidades/Modalidades";
+import CreateModalidad from "./components/modalidades/CrearModalidad";
function App() {
const [viewPageLoader, setViewPageLoader] = useState(false);
@@ -105,6 +106,7 @@ function RoutesApp({ setViewPageLoader }) {
>
} />
} />
+ } />
diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js
index 824fdc9..839e8af 100644
--- a/cosiap_frontend/src/api.js
+++ b/cosiap_frontend/src/api.js
@@ -118,6 +118,43 @@ const api = {
post: (data) => ax.post('api/dynamic-tables/', data),
getById: (id) => ax.get(`api/dynamic-tables/${id}`),
update: (id, data) => ax.put(`api/dynamic-tables/${id}`, data),
+ },
+ dynamicForms: {
+ opciones: {
+ get: () => ax.get('api/formularios/opciones/'),
+ post: (data) => ax.post('api/formularios/opciones/', data),
+ getById: (id) => ax.get(`api/formularios/opciones/${id}`),
+ update: (id, data) => ax.put(`api/formularios/opciones/${id}`, data),
+ },
+ elementos: {
+ get: () => ax.get('api/formularios/elementos'),
+ post: (data) => ax.post('api/formularios/elementos/', data),
+ getById: (id) => ax.get(`api/formularios/elementos/${id}`),
+ update: (id, data) => ax.put(`api/formularios/elementos/${id}`, data),
+ postElementOption: (elementId, optionId) => ax.post(`/api/formularios/elementos/${elementId}/opcion/${optionId}/`),
+ },
+ secciones: {
+ get: () => ax.get('api/formularios/secciones'),
+ post: (data) => ax.post('api/formularios/secciones/', data),
+ getById: (id) => ax.get(`api/formularios/secciones/${id}`),
+ update: (id, data) => ax.put(`api/formularios/secciones/${id}`, data),
+ postSectionElement: (sectionId, elementId) => ax.post(`/api/formularios/secciones/${sectionId}/elementos/${elementId}`),
+ },
+ dynamicForms: {
+ get: () => ax.get('api/formularios'),
+ post: (data) => ax.post('api/formularios/', data),
+ getById: (id) => ax.get(`api/formularios/${id}`),
+ update: (id, data) => ax.put(`api/formularios/${id}`, data),
+ postFormSection: (formId, sectionId) => ax.post(`/api/formularios/${formId}/secciones/${sectionId}`),
+ },
+ respuestas: {
+ get: () => ax.get('api/formularios/respuestas'),
+ post: (data) => ax.post('api/formularios/respuestas/', data),
+ getById: (id) => ax.get(`api/formularios/respuestas/${id}`),
+ },
+ },
+ formatos: {
+ get: () => ax.get('api/plantillas')
}
};
diff --git a/cosiap_frontend/src/components/modalidades/CrearModalidad.css b/cosiap_frontend/src/components/modalidades/CrearModalidad.css
new file mode 100644
index 0000000..4a0598f
--- /dev/null
+++ b/cosiap_frontend/src/components/modalidades/CrearModalidad.css
@@ -0,0 +1,160 @@
+/* Estilos para el card blanco */
+.white-card {
+ background-color: white;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ margin-bottom: 20px;
+}
+
+/* Estilos para las filas del formulario */
+.form-row {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ margin-bottom: 15px;
+}
+
+.form-group {
+ flex: 1;
+ margin-right: 15px;
+ margin-bottom: 15px;
+}
+
+.form-group label {
+ display: block;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.form-group input,
+.form-group textarea,
+.form-group select {
+ width: 100%;
+ padding: 8px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ font-size: 16px;
+}
+
+.form-group input[type="file"] {
+ padding: 0;
+}
+
+textarea {
+ resize: vertical;
+}
+
+/* Estilos para secciones */
+.section {
+ background-color: #f9f9f9;
+ padding: 15px;
+ margin-bottom: 20px;
+ border-radius: 8px;
+ border: 1px solid #ddd;
+}
+
+/* Estilos para elementos */
+.element {
+ background-color: #f2f2f2;
+ padding: 10px;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #ccc;
+}
+
+/* Estilos para opciones */
+.option {
+ background-color: #e9e9e9;
+ padding: 8px;
+ margin-bottom: 8px;
+ border-radius: 4px;
+ border: 1px solid #bbb;
+}
+
+/* Botones */
+.add-button {
+ background-color: #696d69;
+ color: white;
+ border: none;
+ padding: 8px 12px;
+ font-size: 14px;
+ border-radius: 16px;
+ cursor: pointer;
+ margin-top: 10px;
+ margin-right: 10px;
+}
+
+.add-button:hover {
+ background-color: #45a049;
+}
+
+.delete-button {
+ background-color: #f44336;
+ color: white;
+ border: none;
+ padding: 8px 12px;
+ font-size: 14px;
+ border-radius: 16px;
+ cursor: pointer;
+ margin-top: 10px;
+ margin-right: 10px;
+}
+
+.delete-button:hover {
+ background-color: #e53935;
+}
+
+/* Botón de envío */
+.submit-button {
+ background-color: #4fb659;
+ color: white;
+ padding: 10px 20px;
+ font-size: 16px;
+ border: none;
+ border-radius: 16px;
+ cursor: pointer;
+ margin-top: 20px;
+}
+
+.submit-button:hover {
+ background-color: #19d2b0;
+}
+
+/* Checkbox más pequeño */
+input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ margin-right: 10px;
+}
+
+
+.subtitle-text {
+ font-size: 20px;
+ color: #c65f5f;
+}
+
+.subtitle-text-1 {
+ font-size: 18px;
+ color: #752323;
+}
+
+.subtitle-text-2 {
+ font-size: 16px;
+ color: #9e5f5f;
+}
+
+/* Ajustar márgenes y tamaño del texto en etiquetas */
+.form-group label {
+ font-size: 16px;
+ color: #333;
+ margin-bottom: 8px;
+}
+
+.button-container {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+ padding: 0 20px;
+}
diff --git a/cosiap_frontend/src/components/modalidades/CrearModalidad.jsx b/cosiap_frontend/src/components/modalidades/CrearModalidad.jsx
new file mode 100644
index 0000000..fb4ea6c
--- /dev/null
+++ b/cosiap_frontend/src/components/modalidades/CrearModalidad.jsx
@@ -0,0 +1,461 @@
+import { useState, useEffect } from "react";
+import api from '../../api';
+import './CrearModalidad.css'
+import MainContainer from "../common/utility/MainContainer";
+
+// componente para la creación de una modalidad
+// incluyendo la estructura de su formulario dinámico
+const CreateModalidad = () => {
+ const [nombre, setNombre] = useState('');
+ const [descripcion, setDescripcion] = useState('');
+ const [montoMaximo, setMontoMaximo] = useState(0);
+ const [mostrar, setMostrar] = useState(true);
+ const [archivado, setArchivado] = useState(false);
+ const [imagen, setImagen] = useState(null);
+ const [formName, setFormName] = useState('');
+ const [sections, setSections] = useState([]);
+ const [formatos, setFormatos] = useState([]);
+
+ // Obtener los formatos disponibles al cargar el componente
+ useEffect(() => {
+ const fetchFormatos = async () => {
+ try {
+ const response = await api.formatos.get();
+ console.log(response.data); // Verifica la estructura de la respuesta
+ setFormatos(response.data || []); // Asegúrate de que `data` sea un array
+ } catch (error) {
+ console.error("Error al obtener los formatos", error);
+ setFormatos([]);
+ }
+ };
+ fetchFormatos();
+ }, []);
+
+
+ // manejamos los cambios de estado de las imagenes.
+ const handleImageChange = (e) => {
+ setImagen(e.target.files[0]);
+ };
+
+ // agregar una seccion a el form
+ const addSection = () => {
+ setSections([...sections, {name:'', elements:[]}]);
+ };
+
+ // actualizar el nombre de una sección
+ const updateSectionName = (index, name) => {
+ const newSections = [...sections];
+ newSections[index].name = name;
+ setSections(newSections);
+ };
+
+ // Eliminar una sección del formulario
+ const removeSection = (index) => {
+ const newSections = sections.filter((_, i) => i !== index);
+ setSections(newSections);
+ };
+
+ // Actualizar el orden de una sección
+ const updateSectionOrden = (index, orden) => {
+ const newSections = [...sections];
+ newSections[index].orden = Number(orden); // Convertir a número
+ setSections(newSections);
+ };
+
+ // Añadir un elemento a una sección
+ const addElementToSection = (sectionIndex) => {
+ const newElement = {name:'', type:'texto_corto', options:[]};
+ const newSections = [...sections];
+ newSections[sectionIndex].elements.push(newElement);
+ setSections(newSections);
+ };
+
+ // actualizar un elemento
+ const updateElement = (sectionIndex, elementIndex, key, value) => {
+ const newSections = [...sections];
+ if (key === 'orden') {
+ newSections[sectionIndex].elements[elementIndex][key] = Number(value); // Convertir a número
+ } else {
+ newSections[sectionIndex].elements[elementIndex][key] = value;
+ }
+ setSections(newSections);
+ };
+
+ // Eliminar un elemento de una sección.
+ const removeElementFromSection = (sectionIndex, elementIndex) => {
+ const newSections = [...sections];
+ newSections[sectionIndex].elements = newSections[sectionIndex].elements.filter((_, i) => i !== elementIndex);
+ setSections(newSections);
+ };
+
+ // Agregar una opción a un elemento
+ const addOptionToElement = (sectionIndex, elementIndex) => {
+ const newOption = { name: '', orden: 0 };
+ const newSections = [...sections];
+ newSections[sectionIndex].elements[elementIndex].options.push(newOption);
+ setSections(newSections);
+ };
+
+
+ // actualizar una opción
+ const updateOption = (sectionIndex, elementIndex, optionIndex, key, value) => {
+ const newSections = [...sections];
+ if (key === 'orden') {
+ newSections[sectionIndex].elements[elementIndex].options[optionIndex][key] = Number(value); // Convertir a número
+ } else {
+ newSections[sectionIndex].elements[elementIndex].options[optionIndex][key] = value;
+ }
+ setSections(newSections);
+ };
+
+ // Elminar una opción de un elemento
+ const removeOptionFromElement = (sectionIndex, elementIndex, optionIndex) => {
+ const newSections = [...sections];
+ newSections[sectionIndex].elements[elementIndex].options = newSections[sectionIndex].elements[elementIndex].options.filter((_, i) => i !== optionIndex);
+ setSections(newSections);
+ };
+
+
+ // manejar el orden de las peticiones creando un flujo correcto.
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ try{
+ // 1. Creamos el fomrulario dinámico
+ const formResponse = await api.dynamicForms.dynamicForms.post({nombre: formName});
+ const FormId = formResponse.data.data.id; // Verifica que el ID se obtenga correctamente
+
+ // 2. Creamos las secciones del formulario
+ const sectionPromises = sections.map(section =>
+ api.dynamicForms.secciones.post({nombre: section.name})
+ );
+ const sectionResponses = await Promise.all(sectionPromises);
+ const sectionIds = sectionResponses.map(res => res.data.data.id);
+
+ // 3. Asociamos las secciones con el formulario creado
+ const formSectionsPromises = sectionIds.map((sectionId, index) =>
+ api.dynamicForms.dynamicForms.postFormSection(FormId, sectionId, {orden: sections[index].orden,})
+ );
+ await Promise.all(formSectionsPromises);
+
+ // 4. Creamos los elementos y los asociamos a las secciones
+ for (const [sectionIndex, section] of sections.entries()){
+ const elementPromises = section.elements.map(element =>
+ api.dynamicForms.elementos.post({nombre: element.name, tipo: element.type, formato: element.formato})
+ );
+ const elementosResponses = await Promise.all(elementPromises);
+ const elementIds = elementosResponses.map(res => res.data.data.id);
+
+ const elementAssociationPromises = elementIds.map((elementId, elementIndex) =>
+ api.dynamicForms.secciones.postSectionElement(sectionIds[sectionIndex], elementId, {orden: section.elements[elementIndex].orden,})
+ )
+ await Promise.all(elementAssociationPromises);
+
+ // 5. Creamos las opciones para los elementos que lo requieran
+ for (const [elementIndex, element] of section.elements.entries()) {
+ if (element.type === 'opcion_multiple' || element.type === 'desplegable' || element.type === 'casillas') {
+ const optionPromises = element.options.map(option =>
+ api.dynamicForms.opciones.post({ nombre: option })
+ );
+ const optionResponses = await Promise.all(optionPromises);
+ const optionIds = optionResponses.map(res => res.data.data.id);
+
+ const elementOptionPromises = optionIds.map((optionId, optionIndex) =>
+ api.dynamicForms.elementos.postElementOption(elementIds[elementIndex], optionId, {orden: element.options[optionIndex].orden,})
+ );
+ await Promise.all(elementOptionPromises);
+ }
+ }
+ }
+ // 6. Creamos la modalidad y le asociamos el form
+ const formData = new FormData();
+ formData.append('nombre', nombre);
+ formData.append('descripcion', descripcion)
+ formData.append('monto_maximo', montoMaximo);
+ formData.append('mostrar', mostrar);
+ formData.append('archivado', archivado);
+ if (imagen){
+ formData.append('imagen', imagen);
+ }
+ formData.append('dynamic_form', FormId);
+ await api.modalidades.post(formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+ alert('Modalidad creada con éxito');
+ } catch (error){
+ console.error('Error al crear la modalidad.', error);
+ alert('Ocurrió un error al crear la modalidad.')
+ }
+ };
+ return(
+
+
+
+ );
+};
+
+export default CreateModalidad;
\ No newline at end of file
diff --git a/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx b/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
index 318e22a..a3a939b 100644
--- a/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
+++ b/cosiap_frontend/src/components/modalidades/ListaModalidades.jsx
@@ -4,10 +4,13 @@ import { ModalidadCard } from "@/components/modalidades/ModalidadCard";
import api from "@/api";
import { useState, useEffect } from "react";
import MainContainer from "@/components/common/utility/MainContainer";
+import './CrearModalidad.css'
export default function ListaModalidades({
setViewPageLoader,
handleModalidadDetail,
+ isAdmin,
+ handleModalidadCreate,
}) {
const [modalidades, setModalidades] = useState([]);
@@ -50,7 +53,9 @@ export default function ListaModalidades({
bg-[var(--principal-c)] text-[var(--principal-mf)] border border-[var(--principal-f)] font-bold text-center uppercase align-middle rounded-lg"
onClick={() => handleModalidadDetail(item.id)}
>
- Solicitar
+ { isAdmin ? "Editar": "Solicitar"
+
+ }
@@ -62,6 +67,15 @@ export default function ListaModalidades({
)}
+
+ { isAdmin &&
+
+ }
+
);
}
diff --git a/cosiap_frontend/src/components/modalidades/Modalidades.jsx b/cosiap_frontend/src/components/modalidades/Modalidades.jsx
index 767dbc5..e9f4e45 100644
--- a/cosiap_frontend/src/components/modalidades/Modalidades.jsx
+++ b/cosiap_frontend/src/components/modalidades/Modalidades.jsx
@@ -1,11 +1,16 @@
import ListaModalidades from "@/components/modalidades/ListaModalidades";
import { useState } from "react";
import Modalidad from "@/components/modalidades/Modalidad";
+import { useAutenticacion } from "../common/utility/Autenticador";
+import CreateModalidad from "./CrearModalidad";
export default function Modalidades({ setViewPageLoader }) {
const [viewModalidades, setViewModalidades] = useState(true);
const [viewModalidadDetail, setViewModalidadDetail] = useState(false);
const [selectedId, setSelectedId] = useState(null);
+ const {isAdmin} = useAutenticacion();
+ const [viewModalidadCreate, setViewModalidadCreate] = useState(false)
+
const handleModalidadDetail = (id) => {
setViewModalidades(false);
@@ -13,17 +18,29 @@ export default function Modalidades({ setViewPageLoader }) {
setSelectedId(id);
};
+ const handleModalidadCreate = () => {
+ setViewModalidades(false)
+ setViewModalidadCreate(true)
+ };
+
+
return (
<>
{viewModalidades && (
)}
{viewModalidadDetail && selectedId && (
)}
+ {viewModalidadCreate && (
+
+ )}
>
);
}
--
GitLab