terminado eliminar y corregido bug en ordenación por id

This commit is contained in:
2025-09-28 14:07:05 +02:00
parent 50599cf33e
commit 22198b4f25
6 changed files with 47 additions and 13 deletions

View File

@ -103,7 +103,7 @@ public class SecurityConfig {
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/", true)
.defaultSuccessUrl("/", false)
.failureUrl("/login?error") // útil para diagnosticar
)

View File

@ -35,7 +35,7 @@ public class User {
@Column(name = "enabled")
private boolean enabled;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new java.util.HashSet<>();

View File

@ -1,6 +1,7 @@
package com.imprimelibros.erp.users;
import com.imprimelibros.erp.datatables.DataTablesResponse;
import com.imprimelibros.erp.i18n.TranslationService;
import com.imprimelibros.erp.users.validation.UserForm;
import jakarta.servlet.http.HttpServletRequest;
@ -22,7 +23,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.imprimelibros.erp.datatables.DataTablesRequest;
@ -32,7 +32,6 @@ import com.imprimelibros.erp.datatables.DataTable;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Locale;
@ -51,19 +50,35 @@ public class UserController {
private MessageSource messageSource;
private Sanitizer sanitizer;
private PasswordEncoder passwordEncoder;
private TranslationService translationService;
public UserController(UserDao repo, UserService userService, MessageSource messageSource, Sanitizer sanitizer,
PasswordEncoder passwordEncoder, RoleDao roleRepo) {
PasswordEncoder passwordEncoder, RoleDao roleRepo, TranslationService translationService) {
this.repo = repo;
this.messageSource = messageSource;
this.sanitizer = sanitizer;
this.roleRepo = roleRepo;
this.passwordEncoder = passwordEncoder;
this.translationService = translationService;
}
@GetMapping
public String list(Model model, Authentication authentication, Locale locale) {
List<String> keys = List.of(
"usuarios.delete.title",
"usuarios.delete.text",
"usuarios.eliminar",
"usuarios.delete.button",
"app.yes",
"app.cancelar",
"usuarios.delete.ok.title",
"usuarios.delete.ok.text"
);
Map<String, String> translations = translationService.getTranslations(locale, keys);
model.addAttribute("languageBundle", translations);
return "imprimelibros/users/users-list";
}
@ -79,7 +94,7 @@ public class UserController {
// Si 'role' es relación, sácalo de aquí:
List<String> searchable = List.of("fullName", "userName", "enabled", "rolesConcat"); // <- busca por roles de
// verdad
List<String> orderable = List.of("fullName", "userName", "enabled", "roleRank"); // <- permite ordenar por estas
List<String> orderable = List.of("id", "fullName", "userName", "enabled", "roleRank"); // <- permite ordenar por estas
// columnas
Specification<User> base = (root, query, cb) -> cb.conjunction();

View File

@ -46,4 +46,9 @@ usuarios.error.delete-self=No se puede eliminar a sí mismo.
usuarios.exito.creado=Usuario creado con éxito.
usuarios.exito.actualizado=Usuario actualizado con éxito.
usuarios.exito.eliminado=Usuario eliminado con éxito.
usuarios.exito.eliminado=Usuario eliminado con éxito.
usuarios.delete.title=Eliminar usuario
usuarios.delete.button=Si, ELIMINAR
usuarios.delete.text=¿Está seguro de que desea eliminar al usuario?<br>Esta acción no se puede deshacer.
usuarios.delete.ok.title=Usuario eliminado
usuarios.delete.ok.text=El usuario ha sido eliminado con éxito.

View File

@ -84,13 +84,17 @@ $(() => {
const id = $(this).data('id');
Swal.fire({
title: '¿Eliminar usuario?',
text: 'Esta acción no se puede deshacer.',
title: window.languageBundle.get(['usuarios.delete.title']) || 'Eliminar usuario',
html: window.languageBundle.get(['usuarios.delete.text']) || 'Esta acción no se puede deshacer.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, eliminar',
cancelButtonText: 'Cancelar',
reverseButtons: true
buttonsStyling: false,
customClass: {
confirmButton: 'btn btn-danger w-xs mt-2',
cancelButton: 'btn btn-light w-xs mt-2'
},
confirmButtonText: window.languageBundle.get(['usuarios.delete.button']) || 'Eliminar',
cancelButtonText: window.languageBundle.get(['app.cancelar']) || 'Cancelar',
}).then((result) => {
if (!result.isConfirmed) return;
@ -98,7 +102,14 @@ $(() => {
url: '/users/' + id,
type: 'DELETE',
success: function () {
Swal.fire({ icon: 'success', title: 'Eliminado', timer: 1200, showConfirmButton: false });
Swal.fire({
icon: 'success', title: window.languageBundle.get(['usuarios.delete.ok.title']) || 'Eliminado',
text: window.languageBundle.get(['usuarios.delete.ok.text']) || 'El usuario ha sido eliminado con éxito.',
showConfirmButton: true,
customClass: {
confirmButton: 'btn btn-secondary w-xs mt-2',
},
});
$('#users-datatable').DataTable().ajax.reload(null, false);
},
error: function (xhr) {

View File

@ -85,6 +85,9 @@
<th:block layout:fragment="modal" />
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
<th:block layout:fragment="pagejs">
<script th:inline="javascript">
window.languageBundle = /*[[${languageBundle}]]*/ {};
</script>
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
<script th:src="@{/assets/js/pages/imprimelibros/users/list.js}"></script>