diff --git a/cosiap_frontend/src/App.css b/cosiap_frontend/src/App.css index c6e36f4dcd91d178bc6098eb323da433fffcca6f..6551f0beb690c4ec7b5fb17d8038c5cf51d1686c 100644 --- a/cosiap_frontend/src/App.css +++ b/cosiap_frontend/src/App.css @@ -521,42 +521,79 @@ font-size: 16px; } /* Estilos de las cards de las secciones */ - .section-card { - background-color: var(--blanco); - width: 90%; - margin: 20px 0; - padding: 20px; - border-radius: 20px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - } - - .section-card h3 { - font-size: 1.8rem; - font-weight: bold; - margin-bottom: 10px; - color: brown; - text-align: center; - } - - /* Contenedor de los elementos, para agruparlos en filas */ - .elementos-container { - display: flex; - flex-wrap: wrap; - gap: 20px; - justify-content: space-between; - } +.section-card { + background-color: var(--blanco); + width: 90%; + margin: 20px auto; /* Centrar las cards */ + padding: 25px; + border-radius: 15px; + border: 2px solid #b4756e; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out; +} + +.section-card h3 { + font-size: 2rem; + font-weight: 600; + margin-bottom: 15px; + color: brown; + text-align: center; + position: relative; + padding-bottom: 10px; +} + +/* Línea decorativa debajo del h3 */ +.section-card h3::after { + content: ""; + position: absolute; + width: 50%; + height: 3px; + background-color: brown; + bottom: 0; + left: 25%; + border-radius: 2px; +} + +/* Contenedor de los elementos, para agruparlos en filas */ +.elementos-container { + display: flex; + flex-wrap: wrap; + gap: 25px; + justify-content: space-between; + padding-top: 10px; +} - /* Cada elemento tiene su propia card */ - .element-card { - background-color: var(--gris-claro); - border-radius: 10px; - padding: 15px; - width: 30%; /* Toma 30% del ancho para 3 elementos por fila */ - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); - display: flex; - flex-direction: column; - justify-content: space-between; - } +/* Cada elemento tiene su propia card */ +.element-card { + background-color: var(--gris-claro); + border-radius: 12px; + padding: 20px; + width: 30%; /* Toma 30% del ancho para 3 elementos por fila */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + justify-content: space-between; + transition: border 0.3s ease-in-out, transform 0.2s ease-in-out; +} + +/* Efecto hover para dar más dinamismo */ +.element-card:hover { + transform: scale(1.02); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +/* Estilo para el borde según el estado */ +.element-card.revisando { + border: 3px solid yellow; +} + +.element-card.valido { + border: 3px solid green; +} + +.element-card.invalido { + border: 3px solid red; +} /* Estilos para los inputs dentro de las element-cards */ .element-card input, @@ -580,6 +617,7 @@ font-size: 16px; .monto-card { background-color: var(--blanco); width: 90%; + border: 2px solid #b4756e; margin: 30px 0; padding: 30px; border-radius: 20px; @@ -717,40 +755,51 @@ font-size: 16px; /* Borde dependiendo del estado */ .border-green { border: 4px solid rgb(19, 183, 19); + transition: border-color 0.3s, box-shadow 0.3s; +} + +.border-green:hover { + box-shadow: 0 0 10px rgba(19, 183, 19, 0.5); } .border-yellow { border: 4px solid rgb(221, 221, 111); + transition: border-color 0.3s, box-shadow 0.3s; +} + +.border-yellow:hover { + box-shadow: 0 0 10px rgba(221, 221, 111, 0.5); } .border-red { border: 4px solid rgb(206, 71, 71); + transition: border-color 0.3s, box-shadow 0.3s; } -/* Texto de la observación */ -.observacion-text { - margin-top: 5px; - display: block; +.border-red:hover { + box-shadow: 0 0 10px rgba(206, 71, 71, 0.5); } -.border-green .observacion-text { - color: green; +/* Contenedor de los inputs con borde */ +.element-container { + margin-bottom: 15px; + padding: 10px; + border-radius: 8px; /* Mayor radio para bordes más suaves */ + background-color: #f9f9f9; /* Color de fondo suave */ + transition: background-color 0.3s; } -.border-yellow .observacion-text { - color: yellow; +.element-container:hover { + background-color: #f1f1f1; /* Color de fondo más claro al pasar el ratón */ } -.border-red .observacion-text { - color: red; +/* Media Queries para responsividad */ +@media (max-width: 768px) { + .element-container { + padding: 15px; /* Más espacio en pantallas pequeñas */ + } } -/* Contenedor de los inputs con borde */ -.element-container { - margin-bottom: 15px; - padding: 10px; - border-radius: 5px; -} /* Otros estilos de inputs */ .input-class, diff --git a/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx b/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx index 2482832ccc96ca2f41008abe06eb0833c641d4e7..5b59129c9579625b4df5b995aef5dd1e71c12f23 100644 --- a/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx +++ b/cosiap_frontend/src/components/common/utility/RenderElementEdit.jsx @@ -16,23 +16,18 @@ export const renderElemento = (seccionId, elemento, handleInputChange, handleChe console.log(status, observacion) // Determinamos el borde del color del card basado en el status - let borderColorClass = ""; let estado = ""; switch (status) { case "valido": - borderColorClass = "border-green"; - estado = "Aprobado" // borde verde para válido + estado = "Aprobado" break; case "revisando": - borderColorClass = "border-yellow"; // borde amarillo para revisando estado = "En revisión" break; case "invalido": - borderColorClass = "border-red"; // borde rojo para inválido + estado = "Documento Incorrecto" break; - default: - borderColorClass = ""; // sin borde especial si no hay estado } const handleChange = (e) => { @@ -42,7 +37,7 @@ export const renderElemento = (seccionId, elemento, handleInputChange, handleChe }; const renderInput = (inputElement, obligatorio) => ( -
+
{inputElement} {obligatorio && *obligatorio}

diff --git a/cosiap_frontend/src/components/common/utility/RenderElementView.jsx b/cosiap_frontend/src/components/common/utility/RenderElementView.jsx index 30d28659eeb75b82708c16dacefc9cb3fec90b2b..09ce61df4ed98a3e70bc1d8dcd09e16b29361abb 100644 --- a/cosiap_frontend/src/components/common/utility/RenderElementView.jsx +++ b/cosiap_frontend/src/components/common/utility/RenderElementView.jsx @@ -13,29 +13,22 @@ export const renderElemento = (seccionId, elemento, handleInputChange, handleChe const status = respuestaElemento ? respuestaElemento.status : ""; const observacion = respuestaElemento ? respuestaElemento.observacion : ""; - // Determinamos el borde del color del card basado en el status - let borderColorClass = ""; let estado = ""; switch (status) { case "valido": - borderColorClass = "border-green"; estado = "Aprobado" // borde verde para válido break; case "revisando": - borderColorClass = "border-yellow"; // borde amarillo para revisando estado = "En revisión" break; case "invalido": - borderColorClass = "border-red"; // borde rojo para inválido estado = "Documento Incorrecto" break; - default: - borderColorClass = ""; // sin borde especial si no hay estado } const renderInput = (inputElement, obligatorio) => ( -
+
{inputElement} {obligatorio && *obligatorio}

diff --git a/cosiap_frontend/src/components/modalidades/EditarModalidad.jsx b/cosiap_frontend/src/components/modalidades/EditarModalidad.jsx index b67cec5f12b29fbae8ab59bc59db6307c0c7868d..016b301ad4a0f72a271fbc96283f611358c76606 100644 --- a/cosiap_frontend/src/components/modalidades/EditarModalidad.jsx +++ b/cosiap_frontend/src/components/modalidades/EditarModalidad.jsx @@ -122,27 +122,30 @@ const EditModalidad = () => { showAlert('Por favor, ingrese un nombre para la nueva sección.', false); return; } - - // Hacer una solicitud POST para agregar la nueva sección + const response = await api.dynamicForms.secciones.post({ nombre: newSectionName }); await api.dynamicForms.dynamicForms.postFormSection(formId, response.data.data.id); - - // Actualizar el estado con la nueva sección + + // Actualizamos el estado de las secciones setSections(prevSections => ({ ...prevSections, - [response.data.data.id]: { ...response.data.data, elementos: [] } // Inicializar con un arreglo vacío de elementos + [response.data.data.id]: { + ...response.data.data, + elementos: {} // Inicializamos los elementos como objeto vacío + } })); - + // Limpiar el input y ocultar el campo setNewSectionName(''); setIsAddingSection(false); - await handleSubmit(); - window.location.reload(); + + showAlert('Sección agregada exitosamente.', true); } catch (error) { console.error('Error al agregar la sección', error); showAlert('Ocurrió un error al agregar la sección.', false); } }; + // Agregamos un nuevo elemento a la sección @@ -152,55 +155,94 @@ const EditModalidad = () => { showAlert('Por favor, ingrese un nombre para el nuevo elemento.', false); return; } - + const elementType = newElementType || 'texto_corto'; const response = await api.dynamicForms.elementos.post({ nombre: newElementName, tipo: elementType, + obligatorio: newElemetObligatorio, formato: newElementFormat, }); - + await api.dynamicForms.secciones.postSectionElement(sectionId, response.data.data.id); - + + // Actualizamos el estado agregando el nuevo elemento en la sección correspondiente + setSections(prevSections => ({ + ...prevSections, + [sectionId]: { + ...prevSections[sectionId], + elementos: { + ...prevSections[sectionId].elementos, + [response.data.data.id]: { ...response.data.data, opciones: {} } + } + } + })); + // Limpiar el input y ocultar el campo setNewElementName(''); setSelectedSectionId(null); setIsAddingElement(false); - await handleSubmit(); - window.location.reload(); + + showAlert('Elemento agregado exitosamente.', true); } catch (error) { console.error('Error al agregar el elemento', error); showAlert('Ocurrió un error al agregar el elemento.', false); } }; + // Agregamos una nueva opción a un elemento - const handleAddOption = async (elementId) => { + const handleAddOption = async (sectionId, elementId) => { try { if (!newOptionName) { showAlert('Por favor, ingrese un nombre para la nueva opción.', false); return; } - - // Hacer una solicitud POST para agregar la nueva opción - const response = await api.dynamicForms.opciones.post({ - nombre: newOptionName, - }); - + + const response = await api.dynamicForms.opciones.post({ nombre: newOptionName }); await api.dynamicForms.elementos.postElementOption(elementId, response.data.data.id); - + + // Actualizamos el estado validando la existencia del elemento + setSections(prevSections => { + const updatedSections = { ...prevSections }; + const section = updatedSections[sectionId]; + + if (!section) { + console.error(`Sección con ID ${sectionId} no encontrada.`); + showAlert('Sección no encontrada.', false); + return prevSections; + } + + const element = section.elementos[elementId]; + + if (!element) { + console.error(`Elemento con ID ${elementId} no encontrado.`); + showAlert('Elemento no encontrado.', false); + return prevSections; + } + + // Actualizamos las opciones del elemento + element.opciones = { + ...element.opciones, + [response.data.data.id]: response.data.data, + }; + + return updatedSections; + }); + // Limpiar el input y ocultar el campo setNewOptionName(''); setSelectedElementId(null); setIsAddingOption(false); - await handleSubmit(); - window.location.reload(); - + + showAlert('Opción agregada exitosamente.', true); } catch (error) { - console.error('Error al agregar la opción', error); + console.error('Error al agregar la opción:', error); showAlert('Ocurrió un error al agregar la opción.', false); } }; + + // manejamos la eliminacion de una seccion existente en el formulario @@ -213,6 +255,7 @@ const EditModalidad = () => { return updatedSections; }); + showAlert('Sección eliminada.', true); } catch (error){ alert('Ocurrió un error al eliminar la sección.'); } @@ -220,60 +263,70 @@ const EditModalidad = () => { // manejamos el caso de la eliminacion de un elemento de una sección const removeElementFromSectionAPI = async (sectionId, elementId) => { - try{ + try { await api.dynamicForms.elementos.delete(elementId); - - // Actualizar el estado de las secciones para eliminar el elemento del front + + // Actualizamos el estado de las secciones eliminando el elemento correspondiente setSections(prevSections => { const updatedSections = { ...prevSections }; - const updatedElements = Object.values(updatedSections[sectionId]?.elementos).filter( - element => element.id !== elementId - ); - - // Si existen elementos, actualizamos la sección - if (updatedElements) { - updatedSections[sectionId].elementos = updatedElements; - } - + const elements = { ...updatedSections[sectionId].elementos }; + + // Eliminar el elemento específico + delete elements[elementId]; + + // Asignamos el nuevo conjunto de elementos a la sección + updatedSections[sectionId].elementos = elements; + return updatedSections; }); - }catch(error){ + + showAlert('Elemento eliminado.', true); + } catch (error) { alert('Ocurrió un error al eliminar el elemento.'); } - } + }; + // manejamos la eliminacion de una opcion de un elemento const removeOptionFromElementAPI = async (sectionId, elementId, optionId) => { - try{ + try { // Llamar al método delete de la API para eliminar la opción del elemento await api.dynamicForms.opciones.delete(optionId); - + // Actualizar el estado de las secciones para eliminar la opción del front setSections(prevSections => { const updatedSections = { ...prevSections }; - - const updatedElements = Object.values(updatedSections[sectionId]?.elementos).map(element => { - if (element.id === elementId) { - // Filtrar las opciones para eliminar la que corresponde - const updatedOptions = Object.values(element.opciones).filter( - option => option.id !== optionId - ); - return { ...element, opciones: updatedOptions }; - } - return element; - }); - - // Si los elementos fueron actualizados, los asignamos nuevamente a la sección - if (updatedElements) { - updatedSections[sectionId].elementos = updatedElements; + + // Verificar que la sección y el elemento existen + const section = updatedSections[sectionId]; + if (!section) { + console.error(`Sección con ID ${sectionId} no encontrada.`); + return prevSections; } - + + const element = section.elementos[elementId]; + if (!element) { + console.error(`Elemento con ID ${elementId} no encontrado.`); + return prevSections; + } + + // Eliminar la opción específica del objeto opciones + const updatedOptions = { ...element.opciones }; + delete updatedOptions[optionId]; + + // Actualizar las opciones en el elemento + element.opciones = updatedOptions; + return updatedSections; }); - }catch (error){ + + showAlert('Opción eliminada.', true); + } catch (error) { + console.error('Error al eliminar la opción:', error); alert('Ocurrió un error al eliminar la opción.'); } - } + }; + // actualizar un elemento const updateElement = (sectionId, elementId, field, value) => { @@ -796,7 +849,7 @@ const EditModalidad = () => { diff --git a/cosiap_frontend/src/components/modalidades/Modalidad.jsx b/cosiap_frontend/src/components/modalidades/Modalidad.jsx index 3424c42c1d3977a7d59a5cd680c0704d9c637aeb..f70ca0cdd418835743827a17aa460cbf6178146e 100644 --- a/cosiap_frontend/src/components/modalidades/Modalidad.jsx +++ b/cosiap_frontend/src/components/modalidades/Modalidad.jsx @@ -251,6 +251,7 @@ const SolicitarModalidad = () => { showAlert(`Error: ${specificErrorMessage}`, false); } }; + return ( modalidad && ( diff --git a/cosiap_frontend/src/components/solicitudes/EditarSolicitud.jsx b/cosiap_frontend/src/components/solicitudes/EditarSolicitud.jsx index cc874bf293b912a9a9298f6bffd0e708afa9fb9b..15c61bc9c4bb4485ea09b234dd8858aac9d035c9 100644 --- a/cosiap_frontend/src/components/solicitudes/EditarSolicitud.jsx +++ b/cosiap_frontend/src/components/solicitudes/EditarSolicitud.jsx @@ -181,18 +181,40 @@ const EditarSolicitud = () => {

{seccion.nombre}

- - {Object.values(seccion.elementos).map((elemento) => ( -
+ {Object.values(seccion.elementos).map((elemento) => { + // Obtener el estado correspondiente del elemento en respuesta + const respuestaElement = respuesta.find(item => item.elemento_id === elemento.id); + const status = respuestaElement ? respuestaElement.status : ""; + + // Determinamos el borde del color del card basado en el status + let borderColorClass = ""; + switch (status) { + case "valido": + borderColorClass = "border-green"; // borde verde para válido + break; + case "revisando": + borderColorClass = "border-yellow"; // borde amarillo para revisando + break; + case "invalido": + borderColorClass = "border-red"; // borde rojo para inválido + break; + default: + borderColorClass = ""; // sin borde especial si no hay estado + } + + return ( +

{elemento.nombre}

{renderElemento(seccion.id, elemento, handleInputChange, handleCheckboxChange, respuesta)} -
- ))} +
+ ); + })}
)); }; + const handleSubmit = async () => { console.log("Respuestas:", respuesta); console.log("Secciones: ", secciones); diff --git a/cosiap_frontend/src/components/solicitudes/VerSolicitud.jsx b/cosiap_frontend/src/components/solicitudes/VerSolicitud.jsx index 53334d7ed42281fc6ed995c1768923dda7b889aa..63fa0db57cc5f208005d7be7129d052f54d61da8 100644 --- a/cosiap_frontend/src/components/solicitudes/VerSolicitud.jsx +++ b/cosiap_frontend/src/components/solicitudes/VerSolicitud.jsx @@ -50,16 +50,37 @@ const VisualizarSolicitud = () => {

{seccion.nombre}

- {Object.values(seccion.elementos).map((elemento) => ( -
-

{elemento.nombre}

- {/* Renderizamos el elemento en modo solo lectura */} - {renderElemento(seccion.id, elemento, null, null, respuesta)} -
- ))} + {Object.values(seccion.elementos).map((elemento) => { + // Obtener el estado correspondiente del elemento en respuesta + const respuestaElement = respuesta.find(item => item.elemento_id === elemento.id); + const status = respuestaElement ? respuestaElement.status : ""; + + // Determinamos el borde del color del card basado en el status + let borderColorClass = ""; + switch (status) { + case "valido": + borderColorClass = "border-green"; // borde verde para válido + break; + case "revisando": + borderColorClass = "border-yellow"; // borde amarillo para revisando + break; + case "invalido": + borderColorClass = "border-red"; // borde rojo para inválido + break; + default: + borderColorClass = ""; // sin borde especial si no hay estado + } + + return ( +
+

{elemento.nombre}

+ {renderElemento(seccion.id, elemento, null, null, respuesta)} +
+ ); + })}
- )); + )); }; return ( @@ -79,6 +100,7 @@ const VisualizarSolicitud = () => {

Estatus: {solicitud.status}

+

Monto Solicitado: {solicitud.monto_solicitado}

Monto Aprobado: {solicitud.monto_aprobado}