feat: Centralize table component into NucleoTable
I've created a new reusable table component `NucleoTable.vue` to standardize table implementations across the UI.
Key changes:
- `NucleoTable.vue` (`ui/src/components/ui/NucleoTable.vue`) created with props for columns, items, accent color, and background color name. It supports custom cell rendering and action slots.
- I've implemented basic styling using Tailwind CSS, respecting dynamic accent and background colors from the UI store.
- It declares `editItem` and `deleteItem` events for parent components to handle.
- I've refactored existing table components to use `NucleoTable.vue`:
- `tablaAsistencias.vue`
- `tablaEmpleados.vue`
- `tablaPlanillas.vue`
- `tablaTareas.vue`
- Each refactored table now passes its specific data, column definitions, and styling props to `NucleoTable`. Custom cell rendering and action button logic are maintained using slots, preserving original functionality and styling.
This change reduces code duplication and makes future updates to table structures and styles more manageable. You've confirmed that all tables function and appear as expected after refactoring.
This commit is contained in:
@@ -1,55 +1,43 @@
|
||||
<template>
|
||||
<div class="p-4 sm:p-6 rounded-lg overflow-x-auto" :style="{ backgroundColor: ui.tableBgColorPlanillas }">
|
||||
<table class="min-w-full divide-y divide-[var(--accent-color-planillas)]" :style="{ backgroundColor: ui.tableBgColorPlanillas }">
|
||||
<thead class="divide-y divide-[var(--accent-color-planillas)]" :style="{ backgroundColor: ui.tableBgColorPlanillas }">
|
||||
<tr>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">ID</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Título</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Empleado ID</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Fecha Desde</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Fecha Hasta</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Total</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Estado</th>
|
||||
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 dark:text-slate-400 uppercase tracking-wider">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-[var(--accent-color-planillas)]" :style="{ backgroundColor: ui.tableBgColorPlanillas }">
|
||||
<tr v-if="planillas && planillas.length === 0">
|
||||
<td colspan="8" class="px-6 py-10 text-center text-gray-500 dark:text-slate-400 text-lg">No hay planillas para mostrar.</td>
|
||||
</tr>
|
||||
<tr v-for="planilla in planillas" :key="planilla.id" class="transition-colors duration-150 ease-in-out hover:bg-[var(--accent-color-planillas)]/10 dark:hover:bg-[var(--accent-color-planillas)]/20">
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ planilla.id }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ planilla.titulo }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ planilla.empleado_id }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ formatDate(planilla.fecha_desde) }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ formatDate(planilla.fecha_hasta) }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">{{ formatCurrency(planilla.total) }}</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">
|
||||
<span :class="['px-2.5 py-0.5 rounded-full text-xs font-semibold', getStatusClass(planilla.estado)]">{{ planilla.estado }}</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700 dark:text-slate-300">
|
||||
<div class="flex items-center space-x-2">
|
||||
<button @click="editPlanilla(planilla.id)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-600/20 focus:ring-blue-500 dark:focus:ring-blue-400" title="Editar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /></svg>
|
||||
</button>
|
||||
<button @click="confirmDeletePlanilla(planilla)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-600/20 focus:ring-red-500 dark:focus:ring-red-400" title="Eliminar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12.56 0c1.153 0 2.24.03 3.22.077m3.22-.077L10.879 3.28a2.25 2.25 0 012.244-2.077h.093c.956 0 1.853.543 2.244 2.077L14.74 5.79m-4.858 0l-2.828-2.828A1.875 1.875 0 016.188 2.188l2.828 2.828m6.912 0l2.828-2.828a1.875 1.875 0 00-2.652-2.652L12 5.79M9.26 9h5.48L9.26 9z" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<NucleoTable
|
||||
:columns="columns"
|
||||
:items="props.planillas"
|
||||
accent-color="--accent-color-planillas"
|
||||
table-bg-color-name="tableBgColorPlanillas"
|
||||
>
|
||||
<template #cell-fecha_desde="{ item }">
|
||||
{{ formatDate(item.fecha_desde) }}
|
||||
</template>
|
||||
<template #cell-fecha_hasta="{ item }">
|
||||
{{ formatDate(item.fecha_hasta) }}
|
||||
</template>
|
||||
<template #cell-total="{ item }">
|
||||
{{ formatCurrency(item.total) }}
|
||||
</template>
|
||||
<template #cell-estado="{ item }">
|
||||
<span :class="['px-2.5 py-0.5 rounded-full text-xs font-semibold', getStatusClass(item.estado)]">
|
||||
{{ item.estado }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions="{ item }">
|
||||
<div class="flex items-center space-x-2">
|
||||
<button @click="editPlanilla(item.id)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-600/20 focus:ring-blue-500 dark:focus:ring-blue-400" title="Editar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /></svg>
|
||||
</button>
|
||||
<button @click="confirmDeletePlanilla(item)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-600/20 focus:ring-red-500 dark:focus:ring-red-400" title="Eliminar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12.56 0c1.153 0 2.24.03 3.22.077m3.22-.077L10.879 3.28a2.25 2.25 0 012.244-2.077h.093c.956 0 1.853.543 2.244 2.077L14.74 5.79m-4.858 0l-2.828-2.828A1.875 1.875 0 016.188 2.188l2.828 2.828m6.912 0l2.828-2.828a1.875 1.875 0 00-2.652-2.652L12 5.79M9.26 9h5.48L9.26 9z" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</NucleoTable>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
import NucleoTable from '../ui/NucleoTable.vue'; // Import NucleoTable
|
||||
import { usePlanillasStore } from '../../stores/usePlanillas';
|
||||
import { formatDate, formatCurrency, getStatusClass } from '../../utils/formatters.js';
|
||||
import { useUi } from '../../stores/useUi.js';
|
||||
|
||||
const ui = useUi();
|
||||
// useUi store is now handled by NucleoTable
|
||||
|
||||
const props = defineProps({
|
||||
planillas: {
|
||||
@@ -59,10 +47,20 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['edit']); // Removed 'delete' as it's handled internally
|
||||
|
||||
const emit = defineEmits(['edit']); // Emits 'edit' to parent (PlanillasIndex.vue)
|
||||
const planillasStore = usePlanillasStore();
|
||||
|
||||
const columns = [
|
||||
{ key: 'id', label: 'ID' },
|
||||
{ key: 'titulo', label: 'Título' },
|
||||
{ key: 'empleado_id', label: 'Empleado ID' },
|
||||
{ key: 'fecha_desde', label: 'Fecha Desde' },
|
||||
{ key: 'fecha_hasta', label: 'Fecha Hasta' },
|
||||
{ key: 'total', label: 'Total' },
|
||||
{ key: 'estado', label: 'Estado' },
|
||||
];
|
||||
|
||||
// Keep existing methods
|
||||
const editPlanilla = (id) => {
|
||||
emit('edit', id);
|
||||
};
|
||||
@@ -76,8 +74,6 @@ const confirmDeletePlanilla = (planilla) => {
|
||||
const deletePlanillaInternal = async (id) => {
|
||||
try {
|
||||
await planillasStore.deletePlanilla(id);
|
||||
// Optional: Show success notification
|
||||
// No need to emit 'delete' if the store handles list updates and parent components react to store changes
|
||||
} catch (error) {
|
||||
console.error(`Error deleting planilla with id ${id}:`, error);
|
||||
// Optional: Show error notification
|
||||
|
||||
Reference in New Issue
Block a user