Improved style of several classes

This commit is contained in:
Dennis Eckerskorn 2025-05-23 20:02:09 +02:00
parent c5a98435b3
commit e5cc695fd9
9 changed files with 412 additions and 186 deletions

View File

@ -20,6 +20,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -89,28 +90,31 @@ public class TrainingGroupController {
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
@Operation(summary = "Update an existing training group", description = "Update an existing training group with the specified ID") @Operation(summary = "Actualizar un grupo de entrenamiento existente", description = "Actualiza un grupo de entrenamiento por su ID")
@Transactional @Transactional
@PutMapping("/update/{id}") @PutMapping("/update/{id}")
public ResponseEntity<TrainingGroupDTO> update(@PathVariable Integer id, @RequestBody TrainingGroupDTO dto) { public ResponseEntity<TrainingGroupDTO> update(@PathVariable Integer id, @RequestBody TrainingGroupDTO dto) {
dto.setId(id); dto.setId(id);
Teacher teacher = teacherService.findById(dto.getTeacherId()); Teacher teacher = teacherService.findById(dto.getTeacherId());
Set<Student> students = dto.getStudentIds().stream()
Set<Integer> studentIds = dto.getStudentIds() != null ? dto.getStudentIds() : Collections.emptySet();
Set<Student> students = studentIds.stream()
.map(studentService::findById) .map(studentService::findById)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
TrainingGroup updated = trainingGroupService.update(dto.toEntity(teacher, students)); TrainingGroup updatedGroup = trainingGroupService.update(dto.toEntity(teacher, students));
List<TrainingSession> sessions = updated.getTrainingSessions().stream().toList(); updatedGroup.getTrainingSessions().stream().findFirst().ifPresent(session -> {
if (!sessions.isEmpty()) { session.setDate(updatedGroup.getSchedule());
TrainingSession session = sessions.get(0);
session.setDate(updated.getSchedule());
trainingSessionService.save(session); trainingSessionService.save(session);
} });
return ResponseEntity.ok(new TrainingGroupDTO(updated)); return ResponseEntity.ok(new TrainingGroupDTO(updatedGroup));
} }
@Operation(summary = "Find a training group by ID", description = "Retrieve a training group with the specified ID") @Operation(summary = "Find a training group by ID", description = "Retrieve a training group with the specified ID")
@GetMapping("findById/{id}") @GetMapping("findById/{id}")
public ResponseEntity<TrainingGroupDTO> findGroupById(@PathVariable Integer id) { public ResponseEntity<TrainingGroupDTO> findGroupById(@PathVariable Integer id) {

View File

@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -18,7 +19,7 @@ public class TrainingGroupDTO {
private String level; private String level;
private LocalDateTime schedule; private LocalDateTime schedule;
private Integer teacherId; private Integer teacherId;
private Set<Integer> studentIds; private Set<Integer> studentIds = new HashSet<>();
public TrainingGroupDTO() { public TrainingGroupDTO() {
} }

View File

@ -17,7 +17,7 @@ const TrainingGroupForm = () => {
useEffect(() => { useEffect(() => {
api.get("/teachers/getAll") api.get("/teachers/getAll")
.then((res) => setTeachers(res.data)) .then((res) => setTeachers(res.data))
.catch((err) => console.error("Error loading teachers", err)); .catch((err) => console.error("Error al cargar profesores", err));
}, []); }, []);
const handleChange = (e) => { const handleChange = (e) => {
@ -37,7 +37,7 @@ const TrainingGroupForm = () => {
schedule: formData.schedule, schedule: formData.schedule,
teacherId: parseInt(formData.teacherId), teacherId: parseInt(formData.teacherId),
}); });
setSuccessMsg("Training group created successfully."); setSuccessMsg("✅ Grupo de entrenamiento creado correctamente.");
setFormData({ name: "", level: "", schedule: "", teacherId: "" }); setFormData({ name: "", level: "", schedule: "", teacherId: "" });
} catch (err) { } catch (err) {
console.error("Error al crear grupo", err); console.error("Error al crear grupo", err);
@ -47,25 +47,46 @@ const TrainingGroupForm = () => {
"❌ Error al crear el grupo. Verifica los datos."; "❌ Error al crear el grupo. Verifica los datos.";
setErrorMsg(backendMsg); setErrorMsg(backendMsg);
} }
}; };
return ( return (
<div className="card"> <div className="card">
<h2>Create Training Group</h2> <h2>Crear Grupo de Entrenamiento</h2>
<form onSubmit={handleSubmit} className="form"> <form onSubmit={handleSubmit} className="form-column">
<label>Name:</label> <input
<input type="text" name="name" value={formData.name} onChange={handleChange} required /> type="text"
name="name"
placeholder="Nombre del grupo"
value={formData.name}
onChange={handleChange}
required
/>
<label>Level:</label> <input
<input type="text" name="level" value={formData.level} onChange={handleChange} required /> type="text"
name="level"
placeholder="Nivel"
value={formData.level}
onChange={handleChange}
required
/>
<label>Schedule:</label> <label>Horario del grupo:</label>
<input type="datetime-local" name="schedule" value={formData.schedule} onChange={handleChange} required /> <input
type="datetime-local"
name="schedule"
value={formData.schedule}
onChange={handleChange}
required
/>
<label>Teacher:</label> <select
<select name="teacherId" value={formData.teacherId} onChange={handleChange} required> name="teacherId"
<option value="">-- Select Teacher --</option> value={formData.teacherId}
onChange={handleChange}
required
>
<option value="">-- Selecciona un profesor responsable del grupo --</option>
{teachers.map((teacher) => ( {teachers.map((teacher) => (
<option key={teacher.id} value={teacher.id}> <option key={teacher.id} value={teacher.id}>
{teacher.user?.name} {teacher.user?.surname} {teacher.user?.name} {teacher.user?.surname}
@ -73,12 +94,11 @@ const TrainingGroupForm = () => {
))} ))}
</select> </select>
<button type="submit">Create Group</button> <button type="submit">Crear grupo</button>
</form> </form>
<ErrorMessage message={errorMsg} type="error" /> <ErrorMessage message={errorMsg} type="error" />
<ErrorMessage message={successMsg} type="success" /> <ErrorMessage message={successMsg} type="success" />
</div> </div>
); );
}; };

View File

@ -27,9 +27,9 @@ const TrainingGroupStudentManager = () => {
const group = await api.get(`/training-groups/findById/${groupId}`); const group = await api.get(`/training-groups/findById/${groupId}`);
setGroupStudents(group.data.studentIds || []); setGroupStudents(group.data.studentIds || []);
} catch (err) { } catch (err) {
console.error("Error fetching group students", err); console.error("Error al cargar alumnos del grupo", err);
const msg = const msg =
err.response?.data?.message || "❌ Error al cargar datos del grupo."; err.response?.data?.message || "❌ Error al cargar alumnos del grupo.";
setErrorMsg(msg); setErrorMsg(msg);
setGroupStudents([]); setGroupStudents([]);
} }
@ -40,7 +40,7 @@ const TrainingGroupStudentManager = () => {
await api.put(`/training-groups/assign-student`, null, { await api.put(`/training-groups/assign-student`, null, {
params: { params: {
groupId: selectedGroupId, groupId: selectedGroupId,
studentId: studentId, studentId,
}, },
}); });
setSuccessMsg("✅ Alumno asignado correctamente."); setSuccessMsg("✅ Alumno asignado correctamente.");
@ -60,15 +60,16 @@ const TrainingGroupStudentManager = () => {
await api.put(`/training-groups/remove-student`, null, { await api.put(`/training-groups/remove-student`, null, {
params: { params: {
groupId: selectedGroupId, groupId: selectedGroupId,
studentId: studentId, studentId,
}, },
}); });
setSuccessMsg("Alumno eliminado del grupo."); setSuccessMsg("Alumno eliminado del grupo.");
setErrorMsg(""); setErrorMsg("");
loadGroupStudents(selectedGroupId); loadGroupStudents(selectedGroupId);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
const msg = err.response?.data?.message || "❌ Error al eliminar alumno."; const msg =
err.response?.data?.message || "❌ Error al eliminar el alumno.";
setErrorMsg(msg); setErrorMsg(msg);
setSuccessMsg(""); setSuccessMsg("");
} }
@ -77,11 +78,9 @@ const TrainingGroupStudentManager = () => {
return ( return (
<div className="content-area"> <div className="content-area">
<div className="card"> <div className="card">
<h2>Asignar Alumnos al Grupo de Entrenamiento</h2> <h2>👥 Gestión de Alumnos por Grupo</h2>
<label htmlFor="groupSelect" style={{ fontWeight: "bold" }}> <label htmlFor="groupSelect">🏷 Selecciona un grupo:</label>
🏷 Selecciona un grupo:
</label>
<select <select
id="groupSelect" id="groupSelect"
value={selectedGroupId} value={selectedGroupId}
@ -100,34 +99,42 @@ const TrainingGroupStudentManager = () => {
{selectedGroupId && ( {selectedGroupId && (
<> <>
<hr style={{ margin: "20px 0" }} />
<h3>📋 Alumnos Asignados</h3> <h3>📋 Alumnos Asignados</h3>
<ul> {groupStudents.length === 0 ? (
{groupStudents.length === 0 && <li>No hay alumnos asignados.</li>} <p>No hay alumnos asignados a este grupo.</p>
{groupStudents.map((id) => { ) : (
const student = students.find((s) => s.id === id); <ul className="group-student-list">
return ( {groupStudents.map((id) => {
<li key={id}> const student = students.find((s) => s.id === id);
{student?.user?.name} {student?.user?.surname} return (
<button onClick={() => handleRemove(id)}> <li key={id}>
Eliminar del grupo 🎓 {student?.user?.name} {student?.user?.surname}
</button> <button
</li> className="delete-button"
); onClick={() => handleRemove(id)}
})} >
</ul> Eliminar
</button>
</li>
);
})}
</ul>
)}
<h3> Añadir Alumno</h3> <hr style={{ margin: "20px 0" }} />
<ul> <h3> Añadir Alumnos Disponibles</h3>
<ul className="group-student-list">
{students {students
.filter((s) => !groupStudents.includes(s.id)) .filter((s) => !groupStudents.includes(s.id))
.map((s) => ( .map((s) => (
<li key={s.id}> <li key={s.id}>
{s.user?.name} {s.user?.surname} 👤 {s.user?.name} {s.user?.surname}
<button <button
className="add-button" className="add-button"
onClick={() => handleAssign(s.id)} onClick={() => handleAssign(s.id)}
> >
Añadir al grupo Añadir
</button> </button>
</li> </li>
))} ))}

View File

@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import api from "../../api/axiosConfig"; import api from "../../api/axiosConfig";
import "../styles/ContentArea.css"; import "../styles/ContentArea.css";
import "../styles/Timetable.css";
const ViewTimetable = () => { const ViewTimetable = () => {
const [groups, setGroups] = useState([]); const [groups, setGroups] = useState([]);
@ -9,7 +10,7 @@ const ViewTimetable = () => {
api.get("/training-groups/getAll").then((res) => setGroups(res.data)); api.get("/training-groups/getAll").then((res) => setGroups(res.data));
}, []); }, []);
const days = ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"]; const days = ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"];
const hours = [ const hours = [
"07:00", "08:00", "09:00", "10:00", "11:00", "07:00", "08:00", "09:00", "10:00", "11:00",
"12:00", "13:00", "14:00", "15:00", "16:00", "12:00", "13:00", "14:00", "15:00", "16:00",
@ -19,38 +20,43 @@ const ViewTimetable = () => {
return ( return (
<div className="card"> <div className="card">
<h2>Horario Semanal</h2> <h2>Horario Semanal</h2>
<table className="styled-table" style={{ marginTop: "20px" }}> <div className="timetable-wrapper">
<thead> <table className="styled-table timetable-table">
<tr> <thead>
<th>Hora</th> <tr>
{days.map((day) => ( <th>Hora</th>
<th key={day}>{day}</th> {days.map((day) => (
))} <th key={day}>{day}</th>
</tr>
</thead>
<tbody>
{hours.map((h) => (
<tr key={h}>
<td>{h}</td>
{days.map((_, dayIndex) => (
<td key={h + dayIndex}>
{groups
.filter((g) => {
const date = new Date(g.schedule);
return (
date.getHours() === parseInt(h.split(":")[0]) &&
date.getDay() === dayIndex
);
})
.map((g) => (
<div key={g.id}>{g.name} ({g.level})</div>
))}
</td>
))} ))}
</tr> </tr>
))} </thead>
</tbody> <tbody>
</table> {hours.map((h) => (
<tr key={h}>
<td className="hour-label">{h}</td>
{days.map((_, dayIndex) => (
<td key={h + dayIndex} className="day-cell">
{groups
.filter((g) => {
const date = new Date(g.schedule);
return (
date.getHours() === parseInt(h.split(":")[0]) &&
date.getDay() === dayIndex
);
})
.map((g) => (
<div key={g.id} className="timetable-block">
<strong>{g.name}</strong>
<div className="level-label">{g.level}</div>
</div>
))}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div> </div>
); );
}; };

View File

@ -1,11 +1,15 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import api from "../../api/axiosConfig"; import api from "../../api/axiosConfig";
import ErrorMessage from "../common/ErrorMessage";
import "../styles/ContentArea.css"; import "../styles/ContentArea.css";
const StudentHistoryList = () => { const StudentHistoryList = () => {
const [histories, setHistories] = useState([]); const [histories, setHistories] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(""); const [editingId, setEditingId] = useState(null);
const [editedData, setEditedData] = useState({});
const [errorMsg, setErrorMsg] = useState("");
const [successMsg, setSuccessMsg] = useState("");
useEffect(() => { useEffect(() => {
fetchHistories(); fetchHistories();
@ -15,27 +19,55 @@ const StudentHistoryList = () => {
try { try {
const res = await api.get("/student-history/getAll"); const res = await api.get("/student-history/getAll");
setHistories(res.data); setHistories(res.data);
setLoading(false);
} catch (err) { } catch (err) {
console.error("Error al cargar historial de estudiantes", err); console.error("Error al cargar historial de estudiantes", err);
setError("Error al cargar historial"); setErrorMsg("❌ Error al cargar historial.");
} finally {
setLoading(false); setLoading(false);
} }
}; };
const handleEditClick = (h) => {
setEditingId(h.id);
setEditedData({
eventDate: h.eventDate,
eventType: h.eventType || "",
description: h.description || "",
studentId: h.studentId,
});
};
const handleChange = (e) => {
const { name, value } = e.target;
setEditedData((prev) => ({ ...prev, [name]: value }));
};
const handleUpdate = async () => {
try {
await api.put(`/student-history/update/${editingId}`, editedData);
setSuccessMsg("✅ Evento actualizado correctamente.");
setErrorMsg("");
setEditingId(null);
fetchHistories();
} catch (err) {
console.error(err);
setErrorMsg("❌ Error al actualizar el evento.");
setSuccessMsg("");
}
};
const handleDelete = async (id) => { const handleDelete = async (id) => {
if ( if (!window.confirm("¿Seguro que quieres eliminar este evento del historial?")) return;
!window.confirm("¿Seguro que quieres eliminar este evento del historial?")
)
return;
try { try {
await api.delete(`/student-history/delete/${id}`); await api.delete(`/student-history/delete/${id}`);
setHistories(histories.filter((h) => h.id !== id)); setHistories((prev) => prev.filter((h) => h.id !== id));
alert("Evento eliminado correctamente"); setSuccessMsg("✅ Evento eliminado correctamente.");
setErrorMsg("");
} catch (err) { } catch (err) {
console.error(err); console.error(err);
alert("Error al eliminar el evento"); setErrorMsg("❌ Error al eliminar el evento.");
setSuccessMsg("");
} }
}; };
@ -45,17 +77,14 @@ const StudentHistoryList = () => {
<p>Cargando historial...</p> <p>Cargando historial...</p>
</div> </div>
); );
if (error)
return (
<div className="card">
<p style={{ color: "red" }}>{error}</p>
</div>
);
return ( return (
<div className="card"> <div className="card">
<h2>Historial de Estudiantes</h2> <h2>Historial de Estudiantes</h2>
<ErrorMessage message={errorMsg} type="error" />
<ErrorMessage message={successMsg} type="success" />
{histories.length === 0 ? ( {histories.length === 0 ? (
<p>No hay eventos registrados.</p> <p>No hay eventos registrados.</p>
) : ( ) : (
@ -73,27 +102,53 @@ const StudentHistoryList = () => {
<tbody> <tbody>
{histories.map((h) => ( {histories.map((h) => (
<tr key={h.id}> <tr key={h.id}>
<td> {editingId === h.id ? (
{h.student?.user?.name} {h.student?.user?.surname} <>
{(!h.student || !h.student.user) && `(ID: ${h.studentId})`} <td>
</td> {h.student?.user?.name} {h.student?.user?.surname}
<td>{h.eventDate}</td> </td>
<td>{h.eventType}</td> <td>
<td>{h.description}</td> <input
<td> type="date"
<button name="eventDate"
className="edit-button" value={editedData.eventDate}
onClick={() => alert("Editar aún no implementado")} onChange={handleChange}
> />
</td>
</button> <td>
<button <input
className="delete-button" name="eventType"
onClick={() => handleDelete(h.id)} value={editedData.eventType}
> onChange={handleChange}
🗑 />
</button> </td>
</td> <td>
<input
name="description"
value={editedData.description}
onChange={handleChange}
/>
</td>
<td>
<button className="edit-btn" onClick={handleUpdate}></button>
<button className="delete-btn" onClick={() => setEditingId(null)}></button>
</td>
</>
) : (
<>
<td>
{h.student?.user?.name} {h.student?.user?.surname}
{(!h.student || !h.student.user) && `(ID: ${h.studentId})`}
</td>
<td>{h.eventDate}</td>
<td>{h.eventType}</td>
<td>{h.description}</td>
<td>
<button className="edit-btn" onClick={() => handleEditClick(h)}></button>
<button className="delete-btn" onClick={() => handleDelete(h.id)}>🗑</button>
</td>
</>
)}
</tr> </tr>
))} ))}
</tbody> </tbody>

View File

@ -7,49 +7,93 @@ const TrainingGroupList = () => {
const [groups, setGroups] = useState([]); const [groups, setGroups] = useState([]);
const [teachers, setTeachers] = useState([]); const [teachers, setTeachers] = useState([]);
const [editingGroupId, setEditingGroupId] = useState(null); const [editingGroupId, setEditingGroupId] = useState(null);
const [newTeacherId, setNewTeacherId] = useState(""); const [editedGroup, setEditedGroup] = useState({});
const [errorMsg, setErrorMsg] = useState("");
const [successMsg, setSuccessMsg] = useState("");
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, []); }, []);
const fetchData = async () => { const fetchData = async () => {
const groupRes = await api.get("/training-groups/getAll"); try {
const teacherRes = await api.get("/teachers/getAll"); const groupRes = await api.get("/training-groups/getAll");
setGroups(groupRes.data); const teacherRes = await api.get("/teachers/getAll");
setTeachers(teacherRes.data); setGroups(groupRes.data);
}; setTeachers(teacherRes.data);
} catch (err) {
const handleUpdateTeacher = async (groupId) => { setErrorMsg("❌ Error al cargar los grupos o profesores.");
const group = groups.find((g) => g.id === groupId); console.error(err);
const updatedGroup = {
...group,
teacherId: parseInt(newTeacherId),
};
await api.put(`/training-groups/update/${groupId}`, updatedGroup);
setEditingGroupId(null);
fetchData();
};
const handleDelete = async (groupId) => {
if (window.confirm("Are you sure you want to delete this group?")) {
await api.delete(`/training-groups/delete/${groupId}`);
fetchData();
} }
}; };
const handleEditClick = (group) => {
setEditingGroupId(group.id);
setEditedGroup({
name: group.name,
level: group.level,
schedule: group.schedule.slice(0, 16),
teacherId: group.teacherId,
});
};
const handleChange = (e) => {
const { name, value } = e.target;
setEditedGroup((prev) => ({ ...prev, [name]: value }));
};
const handleUpdate = async (id) => {
try {
await api.put(`/training-groups/update/${id}`, {
...editedGroup,
schedule: new Date(editedGroup.schedule).toISOString(),
teacherId: parseInt(editedGroup.teacherId),
studentIds: []
});
setSuccessMsg("✅ Grupo actualizado correctamente.");
setErrorMsg("");
setEditingGroupId(null);
fetchData();
} catch (err) {
setErrorMsg("❌ Error al actualizar el grupo.");
setSuccessMsg("");
console.error(err);
}
};
const handleDelete = async (id) => {
if (!window.confirm("¿Estás seguro de que deseas eliminar este grupo?")) return;
try {
await api.delete(`/training-groups/delete/${id}`);
setSuccessMsg("✅ Grupo eliminado correctamente.");
fetchData();
} catch (err) {
setErrorMsg("❌ Error al eliminar el grupo.");
console.error(err);
}
};
const getTeacherName = (id) => {
const teacher = teachers.find((t) => t.id === id);
return teacher ? `${teacher.user?.name} ${teacher.user?.surname}` : `(ID ${id})`;
};
return ( return (
<div className="card"> <div className="card">
<h2>Grupos de entrenamiento</h2> <h2>Grupos de Entrenamiento</h2>
<ErrorMessage message={errorMsg} type="error" />
<ErrorMessage message={successMsg} type="success" />
<div className="table-wrapper"> <div className="table-wrapper">
<table className="styled-table"> <table className="styled-table">
<thead> <thead>
<tr> <tr>
<th>ID Grupo</th> <th>ID</th>
<th>Nombre</th> <th>Nombre</th>
<th>Nivel</th> <th>Nivel</th>
<th>Fecha / Hora</th> <th>Horario</th>
<th>Profesor</th> <th>Profesor</th>
<th>Acciones</th> <th>Acciones</th>
</tr> </tr>
@ -57,44 +101,63 @@ const TrainingGroupList = () => {
<tbody> <tbody>
{groups.map((group) => ( {groups.map((group) => (
<tr key={group.id}> <tr key={group.id}>
<td>{group.id}</td> {editingGroupId === group.id ? (
<td>{group.name}</td> <>
<td>{group.level}</td> <td>{group.id}</td>
<td>{new Date(group.schedule).toLocaleString()}</td> <td>
<td> <input
{editingGroupId === group.id ? ( name="name"
<select value={editedGroup.name}
value={newTeacherId} onChange={handleChange}
onChange={(e) => setNewTeacherId(e.target.value)} />
> </td>
<option value="">-- Select --</option> <td>
{teachers.map((teacher) => ( <input
<option key={teacher.id} value={teacher.id}> name="level"
{teacher.user?.name} {teacher.user?.surname} value={editedGroup.level}
</option> onChange={handleChange}
))} />
</select> </td>
) : ( <td>
`${group.teacherId}` <input
)} type="datetime-local"
</td> name="schedule"
<td> value={editedGroup.schedule}
{editingGroupId === group.id ? ( onChange={handleChange}
<button onClick={() => handleUpdateTeacher(group.id)}> />
Save </td>
</button> <td>
) : ( <select
<button name="teacherId"
onClick={() => { value={editedGroup.teacherId}
setEditingGroupId(group.id); onChange={handleChange}
setNewTeacherId(group.teacherId || ""); >
}} <option value="">-- Selecciona profesor --</option>
> {teachers.map((t) => (
Edit Teacher <option key={t.id} value={t.id}>
</button> {t.user?.name} {t.user?.surname}
)} </option>
<button onClick={() => handleDelete(group.id)}>Delete</button> ))}
</td> </select>
</td>
<td>
<button className="edit-btn" onClick={() => handleUpdate(group.id)}></button>
<button className="delete-btn" onClick={() => setEditingGroupId(null)}></button>
</td>
</>
) : (
<>
<td>{group.id}</td>
<td>{group.name}</td>
<td>{group.level}</td>
<td>{new Date(group.schedule).toLocaleString()}</td>
<td>{getTeacherName(group.teacherId)}</td>
<td>
<button className="edit-btn" onClick={() => handleEditClick(group)}></button>
<button className="delete-btn" onClick={() => handleDelete(group.id)}>🗑</button>
</td>
</>
)}
</tr> </tr>
))} ))}
</tbody> </tbody>

View File

@ -264,6 +264,28 @@ form textarea:focus {
outline: none; outline: none;
} }
.group-student-list li {
padding: 10px 12px;
margin-bottom: 6px;
background-color: #ffffff;
border: 1px solid #dcdde1;
border-radius: 10px;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.2s;
}
.group-student-list li:hover {
background-color: #f0f4f8;
}
.group-student-list li .delete-button,
.group-student-list li .add-button {
margin-left: 10px;
}

View File

@ -0,0 +1,48 @@
/* Ajuste general de la tabla */
.timetable-wrapper {
overflow-x: auto;
}
.timetable-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed; /* fuerza a que todas las celdas tengan el mismo ancho */
}
.timetable-table th,
.timetable-table td {
padding: 6px;
text-align: center;
border: 1px solid #dcdde1;
height: 60px; /* ajusta esto si lo quieres más compacto */
vertical-align: middle;
position: relative;
}
/* Hora a la izquierda */
.hour-label {
font-weight: bold;
background-color: #f0f0f0;
width: 70px;
}
/* Celda del día */
.day-cell {
padding: 0;
}
/* Tarjeta dentro de la celda */
.timetable-block {
background-color: #2ecc71;
color: white;
width: 100%;
height: 100%;
font-size: 14px;
padding: 4px;
border-radius: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}