Readjusting styles and adding more implementations and fixing bugs
This commit is contained in:
parent
c817b09c9f
commit
c5a98435b3
|
|
@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -70,7 +71,8 @@ public class NotificationController {
|
|||
|
||||
Set<User> existingUsers = updated.getUsers();
|
||||
if (existingUsers != null) {
|
||||
for (User user : existingUsers) {
|
||||
List<User> clonedUsers = new ArrayList<>(existingUsers);
|
||||
for (User user : clonedUsers) {
|
||||
notificationService.removeNotificationFromUser(updated, user);
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +87,7 @@ public class NotificationController {
|
|||
return ResponseEntity.ok(toDTO(updated));
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "Delete a notification by ID", description = "Delete a notification by its ID")
|
||||
@DeleteMapping("/delete/{id}")
|
||||
public ResponseEntity<String> delete(@PathVariable Integer id) {
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -19,3 +19,4 @@ api.interceptors.request.use(
|
|||
);
|
||||
|
||||
export default api;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import api from "../../api/axiosConfig";
|
||||
|
||||
|
||||
const IVATypeManager = () => {
|
||||
const [ivaTypes, setIvaTypes] = useState([]);
|
||||
const [newIva, setNewIva] = useState({ percentage: "", description: "" });
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import api from '../../api/axiosConfig';
|
||||
import '../styles/FormStyles.css';
|
||||
import '../styles/ContentArea.css';
|
||||
|
||||
const NotificationCreateForm = () => {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
|
@ -82,7 +82,7 @@ const NotificationCreateForm = () => {
|
|||
<div className="card">
|
||||
<h2>Crear nueva notificación</h2>
|
||||
|
||||
<form onSubmit={handleSubmit} className="notification-form">
|
||||
<form onSubmit={handleSubmit} className="form-column">
|
||||
<input type="text" name="title" placeholder="Título" value={notificationData.title} onChange={handleChange} required />
|
||||
<textarea name="message" placeholder="Mensaje" value={notificationData.message} onChange={handleChange} rows="4" required />
|
||||
|
||||
|
|
|
|||
|
|
@ -171,10 +171,8 @@ const UserCreateForm = () => {
|
|||
return (
|
||||
<div className="card">
|
||||
<h2>Crear nuevo usuario</h2>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
style={{ display: "flex", flexDirection: "column", gap: "15px" }}
|
||||
>
|
||||
<form onSubmit={handleSubmit} className="form-column">
|
||||
|
||||
{/* Datos básicos */}
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -264,6 +262,7 @@ const UserCreateForm = () => {
|
|||
<input
|
||||
type="date"
|
||||
name="birthdate"
|
||||
placeholder="Fecha de nacimiento"
|
||||
value={studentData.birthdate}
|
||||
onChange={handleChangeStudent}
|
||||
required
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
// src/components/lists/NotificationList.jsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import api from '../../api/axiosConfig';
|
||||
import '../styles/ContentArea.css'; // Tu estilo general de tarjetas y tablas
|
||||
import React, { useEffect, useState } from "react";
|
||||
import api from "../../api/axiosConfig";
|
||||
import ErrorMessage from "../common/ErrorMessage";
|
||||
import "../styles/ContentArea.css";
|
||||
|
||||
const NotificationList = () => {
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editingId, setEditingId] = useState(null);
|
||||
const [editedData, setEditedData] = useState({});
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
const [successMsg, setSuccessMsg] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetchNotifications();
|
||||
|
|
@ -13,21 +17,77 @@ const NotificationList = () => {
|
|||
|
||||
const fetchNotifications = async () => {
|
||||
try {
|
||||
const res = await api.get('/notifications/getAll');
|
||||
const res = await api.get("/notifications/getAll");
|
||||
setNotifications(res.data);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error('Error al cargar notificaciones', err);
|
||||
console.error("Error al cargar notificaciones", err);
|
||||
setErrorMsg("❌ Error al cargar notificaciones.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <div className="card"><p>Cargando notificaciones...</p></div>;
|
||||
const handleEditClick = (n) => {
|
||||
setEditingId(n.id);
|
||||
setEditedData({
|
||||
title: n.title,
|
||||
message: n.message,
|
||||
shippingDate: n.shippingDate.split("T")[0],
|
||||
type: n.type || "",
|
||||
status: n.status,
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setEditedData((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleUpdate = async () => {
|
||||
try {
|
||||
await api.put(`/notifications/update/${editingId}`, {
|
||||
...editedData,
|
||||
shippingDate: new Date(editedData.shippingDate).toISOString(),
|
||||
});
|
||||
setSuccessMsg("✅ Notificación actualizada correctamente");
|
||||
setErrorMsg("");
|
||||
setEditingId(null);
|
||||
fetchNotifications();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setErrorMsg("❌ Error al actualizar la notificación");
|
||||
setSuccessMsg("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!window.confirm("¿Eliminar esta notificación?")) return;
|
||||
try {
|
||||
await api.delete(`/notifications/delete/${id}`);
|
||||
setNotifications((prev) => prev.filter((n) => n.id !== id));
|
||||
setSuccessMsg("✅ Notificación eliminada");
|
||||
setErrorMsg("");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setErrorMsg("❌ Error al eliminar la notificación");
|
||||
}
|
||||
};
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
<div className="card">
|
||||
<p>Cargando notificaciones...</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="card">
|
||||
<h2>Listado de Notificaciones</h2>
|
||||
|
||||
{/* ✅ Mensajes reutilizables */}
|
||||
<ErrorMessage message={errorMsg} type="error" />
|
||||
<ErrorMessage message={successMsg} type="success" />
|
||||
|
||||
{notifications.length === 0 ? (
|
||||
<p>No hay notificaciones registradas.</p>
|
||||
) : (
|
||||
|
|
@ -40,16 +100,89 @@ const NotificationList = () => {
|
|||
<th>Fecha de Envío</th>
|
||||
<th>Tipo</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{notifications.map((n) => (
|
||||
<tr key={n.id}>
|
||||
{editingId === n.id ? (
|
||||
<>
|
||||
<td>
|
||||
<input
|
||||
name="title"
|
||||
value={editedData.title}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<textarea
|
||||
name="message"
|
||||
rows="2"
|
||||
value={editedData.message}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="date"
|
||||
name="shippingDate"
|
||||
value={editedData.shippingDate}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
name="type"
|
||||
value={editedData.type}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<select
|
||||
name="status"
|
||||
value={editedData.status}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<option value="ACTIVE">Activo</option>
|
||||
<option value="INACTIVE">Inactivo</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button className="edit-btn" onClick={handleUpdate}>
|
||||
✅
|
||||
</button>
|
||||
<button
|
||||
className="delete-btn"
|
||||
onClick={() => setEditingId(null)}
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
</td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<td>{n.title}</td>
|
||||
<td>{n.message}</td>
|
||||
<td>{n.shippingDate.split('T')[0]}</td>
|
||||
<td>{n.shippingDate.split("T")[0]}</td>
|
||||
<td>{n.type}</td>
|
||||
<td>{n.status}</td>
|
||||
<td>
|
||||
<button
|
||||
className="edit-btn"
|
||||
onClick={() => handleEditClick(n)}
|
||||
>
|
||||
✏️
|
||||
</button>
|
||||
<button
|
||||
className="delete-btn"
|
||||
onClick={() => handleDelete(n.id)}
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
</td>
|
||||
</>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import "../styles/ContentArea.css";
|
|||
const UserList = () => {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [editingUserId, setEditingUserId] = useState(null);
|
||||
const [editedUserData, setEditedUserData] = useState({});
|
||||
const [errorMsg, setErrorMsg] = useState("");
|
||||
const [successMsg, setSuccessMsg] = useState("");
|
||||
|
||||
|
|
@ -13,20 +15,6 @@ const UserList = () => {
|
|||
fetchUsers();
|
||||
}, []);
|
||||
|
||||
const formatRoleName = (roleName) => {
|
||||
switch (roleName) {
|
||||
case "ROLE_STUDENT":
|
||||
return "Estudiante";
|
||||
case "ROLE_TEACHER":
|
||||
return "Profesor";
|
||||
case "ROLE_ADMIN":
|
||||
return "Administrador";
|
||||
default:
|
||||
return roleName;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const res = await api.get("/users/getAll");
|
||||
|
|
@ -41,9 +29,45 @@ const UserList = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const formatRoleName = (roleName) => {
|
||||
switch (roleName) {
|
||||
case "ROLE_STUDENT": return "Estudiante";
|
||||
case "ROLE_TEACHER": return "Profesor";
|
||||
case "ROLE_ADMIN": return "Administrador";
|
||||
default: return roleName;
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditClick = (user) => {
|
||||
setEditingUserId(user.id);
|
||||
setEditedUserData({ ...user });
|
||||
};
|
||||
|
||||
const handleEditChange = (e) => {
|
||||
setEditedUserData({
|
||||
...editedUserData,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirmUpdate = async () => {
|
||||
try {
|
||||
const res = await api.put(`/users/update/${editingUserId}`, editedUserData);
|
||||
const updatedUser = res.data;
|
||||
setUsers(users.map((u) => (u.id === updatedUser.id ? updatedUser : u)));
|
||||
setEditingUserId(null);
|
||||
setEditedUserData({});
|
||||
setSuccessMsg("✅ Usuario actualizado correctamente.");
|
||||
setErrorMsg("");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
const msg = err.response?.data?.message || "❌ Error al actualizar el usuario.";
|
||||
setErrorMsg(msg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!window.confirm("¿Estás seguro de que deseas eliminar este usuario?")) return;
|
||||
|
||||
try {
|
||||
await api.delete(`/users/delete/${id}`);
|
||||
setUsers(users.filter((u) => u.id !== id));
|
||||
|
|
@ -77,6 +101,8 @@ const UserList = () => {
|
|||
<th>Nombre</th>
|
||||
<th>Apellido</th>
|
||||
<th>Email</th>
|
||||
<th>Teléfono</th>
|
||||
<th>Dirección</th>
|
||||
<th>Rol</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
|
|
@ -85,15 +111,40 @@ const UserList = () => {
|
|||
<tbody>
|
||||
{users.map((user) => (
|
||||
<tr key={user.id}>
|
||||
{editingUserId === user.id ? (
|
||||
<>
|
||||
<td><input name="name" value={editedUserData.name} onChange={handleEditChange} /></td>
|
||||
<td><input name="surname" value={editedUserData.surname} onChange={handleEditChange} /></td>
|
||||
<td><input name="email" value={editedUserData.email} onChange={handleEditChange} /></td>
|
||||
<td><input name="phoneNumber" value={editedUserData.phoneNumber} onChange={handleEditChange} /></td>
|
||||
<td><input name="address" value={editedUserData.address} onChange={handleEditChange} /></td>
|
||||
<td>{formatRoleName(user.roleName)}</td>
|
||||
<td>
|
||||
<select name="status" value={editedUserData.status} onChange={handleEditChange}>
|
||||
<option value="ACTIVE">Activo</option>
|
||||
<option value="INACTIVE">Inactivo</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button className="edit-btn" onClick={handleConfirmUpdate}>✅</button>
|
||||
<button className="delete-btn" onClick={() => setEditingUserId(null)}>❌</button>
|
||||
</td>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<td>{user.name}</td>
|
||||
<td>{user.surname}</td>
|
||||
<td>{user.email}</td>
|
||||
<td>{user.phoneNumber}</td>
|
||||
<td>{user.address}</td>
|
||||
<td>{formatRoleName(user.roleName)}</td>
|
||||
<td>{user.status}</td>
|
||||
<td>
|
||||
<button className="edit-btn" onClick={() => alert("✏️ Edición no implementada")}>✏️</button>
|
||||
<button className="edit-btn" onClick={() => handleEditClick(user)}>✏️</button>
|
||||
<button className="delete-btn" onClick={() => handleDelete(user.id)}>🗑️</button>
|
||||
</td>
|
||||
</>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -78,7 +78,10 @@ form {
|
|||
gap: 15px;
|
||||
}
|
||||
|
||||
form input, form select {
|
||||
form input,
|
||||
form select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #dcdde1;
|
||||
|
|
@ -87,6 +90,7 @@ form input, form select {
|
|||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
|
||||
form input:focus, form select:focus {
|
||||
border-color: #0984e3;
|
||||
background-color: #ffffff;
|
||||
|
|
@ -212,4 +216,54 @@ ul li button:hover {
|
|||
background-color: #27ae60;
|
||||
}
|
||||
|
||||
.form-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.user-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 12px;
|
||||
padding: 6px 12px;
|
||||
font-size: 15px;
|
||||
background-color: transparent;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.user-checkbox input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: #0984e3;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-checkbox:hover {
|
||||
background-color: #f0f4ff;
|
||||
}
|
||||
|
||||
form textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 14px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #dcdde1;
|
||||
font-size: 16px;
|
||||
background-color: #f5f6fa;
|
||||
transition: border-color 0.3s;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
form textarea:focus {
|
||||
border-color: #0984e3;
|
||||
background-color: #ffffff;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue