Skip to content

Commit 22d6627

Browse files
Merge branch 'development'
2 parents 2165765 + 22fd460 commit 22d6627

3 files changed

Lines changed: 147 additions & 163 deletions

File tree

src/Components/Home/Eventos/eventos.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
"descripcion": "Prepárate para vivir una experiencia única este Halloween académico con “Maestros del Terror”, un evento en el que los estudiantes se convierten en jueces y los maestros en las más temidas leyendas del aula.",
77
"navigate":"/events/halloween",
88
"img": "/assets/events/maestros.png",
9-
"visible": true
9+
"visible": false
1010
}
1111
]

src/Components/Home/Novedades/Novedades.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
"name": "Maestros del Terror",
1919
"img": "/assets/novedades/halloween2.png",
2020
"url": "none",
21-
"visible": true
21+
"visible": false
2222
}
2323
]

src/Components/Reports/FormReports.jsx

Lines changed: 145 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -19,96 +19,83 @@ const FormularioReports = () => {
1919
const [fileList, setFileList] = useState([]);
2020

2121
// Tipos predefinidos de incidentes
22-
const tipos = [
23-
"Infraestructura y mantenimiento",
24-
"Seguridad",
25-
"Salud y bienestar",
26-
"Convivencia",
27-
"Tecnología y conectividad",
28-
"Recursos académicos",
29-
"Gestión administrativa",
30-
"Medio ambiente",
31-
"Comunicación y señalización",
32-
"Otros"
33-
];
34-
22+
const tipos = [
23+
"Infraestructura y mantenimiento",
24+
"Seguridad",
25+
"Salud y bienestar",
26+
"Convivencia",
27+
"Tecnología y conectividad",
28+
"Recursos académicos",
29+
"Gestión administrativa",
30+
"Medio ambiente",
31+
"Comunicación y señalización",
32+
"Otros",
33+
];
3534

3635
// Enviar formulario
37-
// Enviar formulario
38-
const onFinish = async (values) => {
39-
try {
40-
setSubmitting(true);
41-
42-
// Crear FormData para enviar texto y archivo
43-
const formData = new FormData();
44-
formData.append("tipo_incidente", values.tipo_incidente.trim());
45-
formData.append("descripcion", values.descripcion.trim());
46-
47-
// Ubicación requerida
48-
formData.append("ubicacion", values.ubicacion.trim());
49-
50-
// Fecha y hora opcional (usa la actual si no se seleccionó)
51-
// Fecha seleccionada + hora actual del sistema
52-
const fechaHora = values.fecha_hora
53-
? dayjs(values.fecha_hora)
54-
.hour(dayjs().hour()) // hora actual
55-
.minute(dayjs().minute()) // minuto actual
56-
.second(dayjs().second()) // segundo actual
57-
.format("YYYY-MM-DD HH:mm:ss")
58-
: dayjs().format("YYYY-MM-DD HH:mm:ss");
59-
formData.append("fecha_hora", fechaHora);
60-
61-
62-
// Archivo opcional con compresión
63-
if (fileList.length > 0 && fileList[0].originFileObj) {
64-
const compressedFile = await reduceImage(fileList[0].originFileObj, 1024, 1024, 0.7);
65-
formData.append("foto", compressedFile);
66-
}
67-
68-
// Enviar la solicitud
69-
const res = await fetch(`${backend}/reports/add`, {
70-
method: "POST",
71-
body: formData,
72-
});
73-
74-
const data = await res.json().catch(() => ({
75-
message: "Error inesperado en la respuesta del servidor.",
76-
}));
77-
78-
if (res.ok) {
79-
message.success("✅ ¡Reporte enviado correctamente!");
80-
form.resetFields();
81-
setFileList([]);
82-
setShowAnimation(true);
36+
const onFinish = async (values) => {
37+
try {
38+
setSubmitting(true);
39+
40+
const formData = new FormData();
41+
formData.append("tipo_incidente", values.tipo_incidente.trim());
42+
formData.append("descripcion", values.descripcion.trim());
43+
formData.append("ubicacion", values.ubicacion.trim());
44+
45+
// Fecha y hora (combina fecha seleccionada + hora actual)
46+
const fechaHora = values.fecha_hora
47+
? dayjs(values.fecha_hora)
48+
.hour(dayjs().hour())
49+
.minute(dayjs().minute())
50+
.second(dayjs().second())
51+
.format("YYYY-MM-DD HH:mm:ss")
52+
: dayjs().format("YYYY-MM-DD HH:mm:ss");
53+
formData.append("fecha_hora", fechaHora);
54+
55+
// Archivo opcional con compresión
56+
if (fileList.length > 0 && fileList[0].originFileObj) {
57+
const compressedFile = await reduceImage(fileList[0].originFileObj, 1024, 1024, 0.7);
58+
formData.append("foto", compressedFile);
59+
}
8360

84-
// Esperar un poco para mostrar la animación antes de redirigir
85-
setTimeout(() => {
86-
setShowAnimation(false);
87-
navigate("/reports"); // redirige a la lista de reportes
88-
}, 1500);
89-
} else {
90-
console.error("Error del servidor:", data);
91-
Swal.fire("Error", data.message || "Ocurrió un error al enviar el reporte.", "error");
61+
// Enviar solicitud
62+
const res = await fetch(`${backend}/reports/add`, {
63+
method: "POST",
64+
body: formData,
65+
});
66+
67+
const data = await res.json().catch(() => ({
68+
message: "Error inesperado en la respuesta del servidor.",
69+
}));
70+
71+
if (res.ok) {
72+
message.success("✅ ¡Reporte enviado correctamente!");
73+
form.resetFields();
74+
setFileList([]);
75+
setShowAnimation(true);
76+
77+
setTimeout(() => {
78+
setShowAnimation(false);
79+
navigate("/reports");
80+
}, 1500);
81+
} else {
82+
console.error("Error del servidor:", data);
83+
Swal.fire("Error", data.message || "Ocurrió un error al enviar el reporte.", "error");
84+
}
85+
} catch (err) {
86+
console.error("Error en el envío:", err);
87+
Swal.fire("Error", "No se pudo enviar el reporte. Verifica tu conexión y formato de archivo.", "error");
88+
} finally {
89+
setSubmitting(false);
9290
}
93-
} catch (err) {
94-
console.error("Error en el envío:", err);
95-
Swal.fire("Error", "No se pudo enviar el reporte. Verifica tu conexión.", "error");
96-
} finally {
97-
setSubmitting(false);
98-
}
99-
};
100-
101-
91+
};
10292

10393
return (
10494
<div style={{ width: "100%", position: "relative" }}>
10595
{/* Encabezado */}
10696
<div style={{ display: "flex", flexDirection: "column", marginBottom: 20 }}>
107-
<div
108-
style={{ display: "flex", alignItems: "center", cursor: "pointer" }}
109-
110-
>
111-
<ArrowLeftOutlined style={{ fontSize: 24, marginRight: 12, color: "#000" }} onClick={() => navigate(-1)}/>
97+
<div style={{ display: "flex", alignItems: "center", cursor: "pointer" }}>
98+
<ArrowLeftOutlined style={{ fontSize: 24, marginRight: 12, color: "#000" }} onClick={() => navigate(-1)} />
11299
<Title level={3} style={{ margin: 0 }}>
113100
Reportar Incidencia
114101
</Title>
@@ -121,7 +108,7 @@ formData.append("fecha_hora", fechaHora);
121108

122109
{/* Formulario */}
123110
<Form layout="vertical" form={form} onFinish={onFinish}>
124-
{/* Tipo de incidente */}
111+
{/* Tipo */}
125112
<Form.Item
126113
label="Tipo de incidencia"
127114
name="tipo_incidente"
@@ -131,9 +118,7 @@ formData.append("fecha_hora", fechaHora);
131118
placeholder="Selecciona un tipo"
132119
showSearch
133120
optionFilterProp="children"
134-
filterOption={(input, option) =>
135-
option.children.toLowerCase().includes(input.toLowerCase())
136-
}
121+
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
137122
>
138123
{tipos.map((t) => (
139124
<Option key={t} value={t}>
@@ -144,91 +129,90 @@ formData.append("fecha_hora", fechaHora);
144129
</Form.Item>
145130

146131
{/* Descripción */}
147-
<Form.Item
148-
label="Descripción"
149-
name="descripcion"
150-
rules={[{ required: true, message: "Describe la incidencia" }]}
151-
>
152-
<TextArea
153-
rows={4}
154-
placeholder="Describe detalladamente lo ocurrido..."
155-
maxLength={3000}
156-
style={{ resize: "none" }}
157-
/>
132+
<Form.Item label="Descripción" name="descripcion" rules={[{ required: true, message: "Describe la incidencia" }]}>
133+
<TextArea rows={4} placeholder="Describe detalladamente lo ocurrido..." maxLength={3000} style={{ resize: "none" }} />
158134
</Form.Item>
159135

160136
{/* Ubicación */}
161-
<Form.Item label="Ubicación" name="ubicacion" rules={[{ required: true, message: "Indica la ubicación en el campus" }]}>
137+
<Form.Item
138+
label="Ubicación"
139+
name="ubicacion"
140+
rules={[{ required: true, message: "Indica la ubicación en el campus" }]}
141+
>
162142
<Input placeholder="Ej. Pasillo principal, baño del segundo piso, etc." />
163143
</Form.Item>
164144

165-
{/* Fecha y hora (opcional) */}
166-
{/* Fecha y hora (required) */}
167-
<Form.Item
168-
label="Fecha del incidente"
169-
name="fecha_hora"
170-
rules={[{ required: true, message: "Selecciona la fecha del incidente" }]}
171-
>
172-
<DatePicker
173-
style={{ width: "100%" }}
174-
placeholder="Selecciona la fecha del incidente"
175-
/>
176-
</Form.Item>
177-
178-
145+
{/* Fecha */}
146+
<Form.Item
147+
label="Fecha del incidente"
148+
name="fecha_hora"
149+
rules={[{ required: true, message: "Selecciona la fecha del incidente" }]}
150+
>
151+
<DatePicker style={{ width: "100%" }} placeholder="Selecciona la fecha del incidente" />
152+
</Form.Item>
179153

180154
{/* Foto */}
181-
<Form.Item label="Evidencia fotográfica (opcional)">
182-
{/* Input oculto que abre la cámara */}
183-
<input
184-
type="file"
185-
accept="image/*"
186-
capture="environment"
187-
style={{ display: "none" }}
188-
id="cameraInput"
189-
onChange={(e) => {
190-
if (e.target.files.length > 0) {
191-
const file = e.target.files[0];
192-
if (!file.type.startsWith("image/")) {
193-
message.error("Solo se permiten archivos de imagen.");
194-
return;
195-
}
196-
setFileList([{ originFileObj: file, uid: '-1', name: file.name }]);
197-
}
198-
}}
199-
/>
200-
201-
202-
<Upload
203-
beforeUpload={(file) => {
204-
const isImage = file.type.startsWith("image/");
205-
if (!isImage) {
206-
message.error("Solo se permiten archivos de imagen.");
207-
}
208-
return isImage ? true : Upload.LIST_IGNORE; // Ignora archivos no válidos
209-
}}
210-
fileList={fileList}
211-
onChange={({ fileList }) => setFileList(fileList.slice(-1))} // Solo 1 foto
212-
listType="picture"
213-
accept="image/*"
214-
maxCount={1} // Solo 1 imagen
215-
style={{ width: "100%" }}
216-
>
217-
<Button style={{ width: "100%" }} icon={<UploadOutlined />}>Subir desde galería</Button>
218-
</Upload>
219-
220-
221-
<Button
222-
icon={<CameraOutlined />}
223-
type="dashed"
224-
style={{ width: "100%", marginTop: 8 }}
225-
onClick={() => document.getElementById("cameraInput").click()}
226-
>
227-
Tomar foto con cámara
228-
</Button>
229-
</Form.Item>
230-
155+
<Form.Item label="Evidencia fotográfica (opcional)">
156+
{/* Input cámara */}
157+
<input
158+
type="file"
159+
accept="image/*"
160+
capture="environment"
161+
style={{ display: "none" }}
162+
id="cameraInput"
163+
onChange={(e) => {
164+
if (e.target.files.length > 0) {
165+
const file = e.target.files[0];
166+
if (!file.type.startsWith("image/")) {
167+
Swal.fire({
168+
icon: "error",
169+
title: "Archivo no permitido",
170+
text: "Solo se pueden subir archivos de imagen (JPG, PNG, GIF, WEBP).",
171+
confirmButtonColor: "#d33",
172+
});
173+
e.target.value = "";
174+
return;
175+
}
176+
setFileList([{ originFileObj: file, uid: "-1", name: file.name }]);
177+
}
178+
}}
179+
/>
231180

181+
{/* Upload desde galería */}
182+
<Upload
183+
beforeUpload={(file) => {
184+
const isImage = file.type.startsWith("image/");
185+
if (!isImage) {
186+
Swal.fire({
187+
icon: "error",
188+
title: "Archivo no permitido",
189+
text: "Solo se pueden subir archivos de imagen (JPG, PNG, GIF, WEBP).",
190+
confirmButtonColor: "#d33",
191+
});
192+
}
193+
return isImage ? true : Upload.LIST_IGNORE;
194+
}}
195+
fileList={fileList}
196+
onChange={({ fileList }) => setFileList(fileList.slice(-1))}
197+
listType="picture"
198+
accept="image/*"
199+
maxCount={1}
200+
style={{ width: "100%" }}
201+
>
202+
<Button style={{ width: "100%" }} icon={<UploadOutlined />}>
203+
Subir desde galería
204+
</Button>
205+
</Upload>
206+
207+
<Button
208+
icon={<CameraOutlined />}
209+
type="dashed"
210+
style={{ width: "100%", marginTop: 8 }}
211+
onClick={() => document.getElementById("cameraInput").click()}
212+
>
213+
Tomar foto con cámara
214+
</Button>
215+
</Form.Item>
232216

233217
{/* Botón enviar */}
234218
<Form.Item>

0 commit comments

Comments
 (0)