Readjusting styles and adding more implementations and fixing bugs

This commit is contained in:
Dennis Eckerskorn 2025-05-22 21:59:53 +02:00
parent c817b09c9f
commit c5a98435b3
9 changed files with 288 additions and 46 deletions

View File

@ -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.

View File

@ -19,3 +19,4 @@ api.interceptors.request.use(
);
export default api;

View File

@ -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: "" });

View File

@ -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 />

View File

@ -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

View File

@ -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}>
<td>{n.title}</td>
<td>{n.message}</td>
<td>{n.shippingDate.split('T')[0]}</td>
<td>{n.type}</td>
<td>{n.status}</td>
{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.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>

View File

@ -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}>
<td>{user.name}</td>
<td>{user.surname}</td>
<td>{user.email}</td>
<td>{formatRoleName(user.roleName)}</td>
<td>{user.status}</td>
<td>
<button className="edit-btn" onClick={() => alert("✏️ Edición no implementada")}></button>
<button className="delete-btn" onClick={() => handleDelete(user.id)}>🗑</button>
</td>
{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={() => handleEditClick(user)}></button>
<button className="delete-btn" onClick={() => handleDelete(user.id)}>🗑</button>
</td>
</>
)}
</tr>
))}
</tbody>

View File

@ -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;
}