feat(UserManagement): añadida funcionalidad para mostrar listado y cambiar paginación/ordenación

parent 8e37b66b
Showing with 64 additions and 115 deletions
......@@ -25,42 +25,53 @@
</div>
</div>
<div v-if="errorMsg" class="alert alert-danger" role="alert">
{{ errorMsg }}
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-primary-custom">
<tr>
<th scope="col" @click="sortBy('name')" class="clickable-header">
<th scope="col" @click="sortByColumn('name')" class="clickable-header">
Nombre
<i :class="getSortIcon('name')"></i>
<i v-if="userStore.sortBy === 'name'" :class="userStore.sortDirection === 'asc' ? 'bi-caret-up-fill' : 'bi-caret-down-fill'"></i>
<i v-else class="bi-arrow-down-up"></i>
</th>
<th scope="col" @click="sortBy('surname')" class="clickable-header">
<th scope="col" @click="sortByColumn('surname')" class="clickable-header">
Apellidos
<i :class="getSortIcon('surname')"></i>
<i v-if="userStore.sortBy === 'surname'" :class="userStore.sortDirection === 'asc' ? 'bi-caret-up-fill' : 'bi-caret-down-fill'"></i>
<i v-else class="bi-arrow-down-up"></i>
</th>
<th scope="col" @click="sortBy('email')" class="clickable-header">
<th scope="col" @click="sortByColumn('email')" class="clickable-header">
Email
<i :class="getSortIcon('email')"></i>
<i v-if="userStore.sortBy === 'email'" :class="userStore.sortDirection === 'asc' ? 'bi-caret-up-fill' : 'bi-caret-down-fill'"></i>
<i v-else class="bi-arrow-down-up"></i>
</th>
<th scope="col" @click="sortByColumn('role')" class="clickable-header">
Rol
<i v-if="userStore.sortBy === 'role'" :class="userStore.sortDirection === 'asc' ? 'bi-caret-up-fill' : 'bi-caret-down-fill'"></i>
<i v-else class="bi-arrow-down-up"></i>
</th>
<th scope="col">Rol</th>
<th scope="col">Estado</th>
<th scope="col">Fecha desactivado</th>
<th scope="col">Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="user in paginatedUsers" :key="user.id">
<tr v-for="user in userStore.users" :key="user.id">
<td>{{ user.name }}</td>
<td>{{ user.surname }}</td>
<td>{{ user.email }}</td>
<td>{{ user.role }}</td>
<td>
<span v-if="user.active" class="badge bg-custom-active">Activo</span>
<span v-if="!user.deletedAt" class="badge bg-custom-active">Activo</span>
<span v-else class="badge bg-custom-inactive">Inactivo</span>
</td>
<td>{{ user.deactivatedAt ? user.deactivatedAt : '-' }}</td>
<td>{{ user.deletedAt ? user.deletedAt : '-' }}</td>
<td>
<div class="d-flex gap-2">
<button v-if="user.active" @click="deactivateUser(user.id)" class="btn btn-outline-danger btn-sm" title="Desactivar">
<button v-if="!user.deletedAt" @click="deactivateUser(user.id)" class="btn btn-outline-danger btn-sm" title="Desactivar">
<i class="bi bi-person-slash"></i>
</button>
<button v-else @click="reactivateUser(user.id)" class="btn btn-outline-success btn-sm" title="Reactivar">
......@@ -74,17 +85,17 @@
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="small text-muted">Mostrando {{ startIndex + 1 }} a {{ endIndex }} de {{ users.length }} usuarios</div>
<div class="small text-muted">Mostrando {{ startIndex }} a {{ endIndex }} de {{ userStore.totalElements }} usuarios</div>
<nav aria-label="Page navigation">
<ul class="pagination pagination-sm mb-0 pagination-custom">
<li class="page-item" :class="{ 'disabled': currentPage === 1 }">
<a class="page-link" href="#" @click.prevent="prevPage">Anterior</a>
<li class="page-item" :class="{ disabled: userStore.currentPage === 0 }">
<button class="page-link" @click="goToPage(userStore.currentPage - 1)">Anterior</button>
</li>
<li class="page-item" v-for="page in totalPages" :key="page" :class="{ 'active': page === currentPage }">
<a class="page-link" href="#" @click.prevent="goToPage(page)">{{ page }}</a>
<li class="page-item" v-for="page in userStore.totalPages" :key="page" :class="{ active: page - 1 === userStore.currentPage }">
<button class="page-link" @click="goToPage(page - 1)">{{ page }}</button>
</li>
<li class="page-item" :class="{ 'disabled': currentPage === totalPages }">
<a class="page-link" href="#" @click.prevent="nextPage">Siguiente</a>
<li class="page-item" :class="{ disabled: userStore.currentPage === userStore.totalPages - 1 }">
<button class="page-link" @click="goToPage(userStore.currentPage + 1)">Siguiente</button>
</li>
</ul>
</nav>
......@@ -97,113 +108,51 @@
</template>
<script setup>
import { ref, computed } from 'vue';
const users = ref([
{ id: 1, name: 'Adrián', surname: 'García Pérez', email: 'adrian@example.com', role: 'Usuario', active: true, deactivatedAt: null },
{ id: 2, name: 'Sofía', surname: 'Martínez López', email: 'sofia@example.com', role: 'Administrador', active: true, deactivatedAt: null },
{ id: 3, name: 'Pablo', surname: 'Díaz Ruiz', email: 'pablo@example.com', role: 'Usuario', active: false, deactivatedAt: '2024-05-10' },
{ id: 4, name: 'Lucía', surname: 'Fernández Gil', email: 'lucia@example.com', role: 'Usuario', active: true, deactivatedAt: null },
{ id: 5, name: 'Javier', surname: 'Torres Soto', email: 'javier@example.com', role: 'Usuario', active: false, deactivatedAt: '2024-05-15' },
{ id: 6, name: 'Carla', surname: 'Jiménez Vega', email: 'carla@example.com', role: 'Usuario', active: true, deactivatedAt: null },
{ id: 7, name: 'Daniel', surname: 'Sánchez Milla', email: 'daniel@example.com', role: 'Usuario', active: true, deactivatedAt: null },
{ id: 8, name: 'Elena', surname: 'Soto Ramos', email: 'elena@example.com', role: 'Usuario', active: false, deactivatedAt: '2024-05-20' }
]);
// Estado de la paginación y ordenación
const currentPage = ref(1);
const itemsPerPage = ref(5);
const sortKey = ref('name');
const sortOrder = ref('asc');
// Propiedad computada para ordenar los usuarios
const sortedUsers = computed(() => {
return users.value.slice().sort((a, b) => {
const keyA = a[sortKey.value];
const keyB = b[sortKey.value];
if (typeof keyA === 'string') {
const result = keyA.localeCompare(keyB);
return sortOrder.value === 'asc' ? result : -result;
} else {
const result = keyA - keyB;
return sortOrder.value === 'asc' ? result : -result;
}
});
});
// Propiedad computada para la paginación
const paginatedUsers = computed(() => {
const start = (currentPage.value - 1) * itemsPerPage.value;
const end = start + parseInt(itemsPerPage.value);
return sortedUsers.value.slice(start, end);
});
// Propiedad computada para el número total de páginas
const totalPages = computed(() => {
return Math.ceil(users.value.length / itemsPerPage.value);
});
// Propiedades computadas para el rango de la tabla
const startIndex = computed(() => (currentPage.value - 1) * itemsPerPage.value);
const endIndex = computed(() => {
const end = startIndex.value + parseInt(itemsPerPage.value);
return Math.min(end, users.value.length);
import { useUserStore } from '@/stores/userStore';
import { ref, onMounted, watch, computed } from 'vue';
const userStore = useUserStore();
const errorMsg = ref(null);
const itemsPerPage = ref(userStore.pageSize);
onMounted(async () => {
errorMsg.value = null;
try {
await userStore.readAll();
} catch (error) {
errorMsg.value = 'No se pudieron cargar los usuarios. Inténtalo de nuevo más tarde.';
}
});
// Métodos de paginación
// Cambip de página
const goToPage = (page) => {
currentPage.value = page;
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
userStore.readAll(page, itemsPerPage.value, userStore.sortBy, userStore.sortDirection);
};
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
// Cambio de ordenación
const sortByColumn = (column) => {
// Si es la misma columna, invierte la dirección
const newSortDirection = userStore.sortBy === column && userStore.sortDirection === 'asc' ? 'desc' : 'asc';
userStore.readAll(0, itemsPerPage.value, column, newSortDirection)
};
// Método de ordenación
const sortBy = (key) => {
if (sortKey.value === key) {
sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc';
} else {
sortKey.value = key;
sortOrder.value = 'asc';
}
};
// Cambios en itemsPerPage
watch(itemsPerPage, (newSize) => {
userStore.fetchUsers(0, newSize, userStore.sortBy, userStore.sortDirection);
});
// Método para mostrar el icono de ordenación
const getSortIcon = (key) => {
if (sortKey.value !== key) {
return 'bi bi-sort-alpha-down-alt';
}
return sortOrder.value === 'asc' ? 'bi bi-sort-alpha-down' : 'bi bi-sort-alpha-up';
};
// Propiedad computada para el índice de inicio
const startIndex = computed(() => {
if (userStore.totalElements === 0) return 0;
return (userStore.currentPage * userStore.pageSize) + 1;
});
// Métodos para activar/desactivar usuarios
const deactivateUser = (id) => {
const user = users.value.find(u => u.id === id);
if (user) {
user.active = false;
user.deactivatedAt = new Date().toISOString().slice(0, 10);
alert(`Usuario ${user.name} desactivado.`);
}
};
// Propiedad computada para el índice de fin
const endIndex = computed(() => {
const end = (userStore.currentPage + 1) * userStore.pageSize;
return Math.min(end, userStore.totalElements);
});
const reactivateUser = (id) => {
const user = users.value.find(u => u.id === id);
if (user) {
user.active = true;
user.deactivatedAt = null;
alert(`Usuario ${user.name} reactivado.`);
}
};
</script>
<style scoped>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment