This commit introduces a standardized approach to table components within the UI. Key changes include: - Refactored tablaAsistencias.vue, tablaEmpleados.vue, tablaPlanillas.vue, and tablaTareas.vue to use Tailwind CSS for consistent styling. - Defined and applied common Tailwind utility patterns for table structure (container, header, body, rows, cells), action buttons (with SVG icons), and status indicators (badges). - Created a shared utility file at `ui/src/utils/formatters.js` for common functions like date formatting, currency formatting, text truncation, and status class generation, reducing code duplication. - Updated table components to use these shared utility functions. - Updated `ui/README.md` to document the UI modules, the standardized table structure, styling conventions, and usage of utility functions. This standardization enhances code maintainability, improves consistency in the user interface, and provides clear guidelines for future table component development.
86 lines
5.7 KiB
Vue
86 lines
5.7 KiB
Vue
<template>
|
|
<div class="p-4 sm:p-6 bg-white shadow-md rounded-lg overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50">
|
|
<tr>
|
|
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 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 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 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 uppercase tracking-wider">Fecha</th>
|
|
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 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 uppercase tracking-wider">Tipo</th>
|
|
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Precio</th>
|
|
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Planilla ID</th>
|
|
<th class="px-4 py-3 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
<tr v-if="!tareas || tareas.length === 0">
|
|
<td colspan="9" class="px-6 py-10 text-center text-gray-500 text-lg">No hay tareas para mostrar.</td>
|
|
</tr>
|
|
<tr v-for="tarea in tareas" :key="tarea.id" class="hover:bg-gray-100 transition-colors duration-150 ease-in-out">
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.id }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.titulo }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.empleado_id }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ formatDate(tarea.fecha) }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">
|
|
<span :class="['px-2.5 py-0.5 rounded-full text-xs font-semibold', getStatusClass(tarea.estado)]">{{ tarea.estado }}</span>
|
|
</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.tipo || 'N/A' }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.precio != null ? formatCurrency(tarea.precio) : 'N/A' }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">{{ tarea.planilla_id || 'N/A' }}</td>
|
|
<td class="px-4 py-3 sm:px-6 sm:py-4 whitespace-nowrap text-sm text-gray-700">
|
|
<div class="flex items-center space-x-2">
|
|
<button @click="editTarea(tarea.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 hover:text-blue-800 hover:bg-blue-100 focus:ring-blue-500" 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="confirmDeleteTarea(tarea)" 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 hover:text-red-800 hover:bg-red-100 focus:ring-red-500" 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>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { defineProps, defineEmits } from 'vue';
|
|
import { useTareasStore } from '../../stores/useTareas';
|
|
import { formatDate, formatCurrency, getStatusClass } from '../../utils/formatters.js';
|
|
|
|
const props = defineProps({
|
|
tareas: {
|
|
type: Array,
|
|
required: true,
|
|
default: () => [],
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['edit']);
|
|
|
|
const tareasStore = useTareasStore();
|
|
|
|
const editTarea = (id) => {
|
|
emit('edit', id);
|
|
};
|
|
|
|
const confirmDeleteTarea = (tarea) => {
|
|
if (confirm(`¿Está seguro de que desea eliminar la tarea "${tarea.titulo}" (ID: ${tarea.id})?`)) {
|
|
deleteTareaInternal(tarea.id);
|
|
}
|
|
};
|
|
|
|
const deleteTareaInternal = async (id) => {
|
|
try {
|
|
await tareasStore.deleteTarea(id);
|
|
// Optional: Show success notification or emit 'deleted' event
|
|
} catch (error) {
|
|
console.error(`Error deleting tarea with id ${id}:`, error);
|
|
alert('Ocurrió un error al eliminar la tarea.');
|
|
// Optional: Show error notification
|
|
}
|
|
};
|
|
</script>
|