Files
planilla/ui/src/views/empleados/EmpleadosIndex.vue
josedario87 0119a77cb7
Some checks failed
build-and-deploy / filter (push) Successful in 2s
Sync to GitHub / sync (push) Failing after 1s
build-and-deploy / build (push) Successful in 9s
build-and-deploy / deploy (push) Successful in 15s
arreglados url de las api en la UI
2025-05-30 02:48:28 -06:00

209 lines
6.8 KiB
Vue

<template>
<div class="p-6 bg-gray-50 min-h-screen">
<header class="mb-8">
<div class="flex justify-between items-center">
<h1 class="text-4xl font-bold text-gray-800">Gestión de Empleados</h1>
<button
@click="goToCreateEmployee"
class="px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-150 ease-in-out"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline-block mr-2" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
</svg>
Crear Empleado
</button>
</div>
<p class="mt-2 text-gray-600">Visualiza, crea y gestiona los empleados de la organización.</p>
</header>
<div class="mb-6 flex justify-end items-center space-x-3">
<span class="text-sm font-medium text-gray-700">Cambiar Vista:</span>
<button
@click="currentView = 'card'"
:class="[
'px-4 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2',
currentView === 'card'
? 'bg-blue-500 text-white shadow-sm focus:ring-blue-400'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-400',
]"
>
Tarjetas
</button>
<button
@click="currentView = 'table'"
:class="[
'px-4 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2',
currentView === 'table'
? 'bg-blue-500 text-white shadow-sm focus:ring-blue-400'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-400',
]"
>
Tabla
</button>
</div>
<div>
<div v-if="loading" class="text-center py-10">
<p class="text-gray-500 text-xl">Cargando empleados...</p>
<!-- You can add a spinner here -->
</div>
<div v-else-if="error" class="text-center py-10">
<p class="text-red-500 text-xl">Error al cargar los empleados: {{ error }}</p>
</div>
<div v-else>
<!-- Card View -->
<div v-if="currentView === 'card'" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<CardEmpleado v-for="employee in employees" :key="employee.id" :employee="employee" />
<div v-if="employees.length === 0" class="col-span-full text-center py-10">
<p class="text-gray-500 text-xl">No hay empleados para mostrar en la vista de tarjetas.</p>
</div>
</div>
<!-- Table View -->
<div v-if="currentView === 'table'">
<TablaEmpleados :employees="employees" />
<div v-if="employees.length === 0" class="text-center py-10 bg-white shadow-md rounded-lg mt-4">
<p class="text-gray-500 text-xl">No hay empleados para mostrar en la vista de tabla.</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import CardEmpleado from '@/components/empleados/cardEmpleado.vue' // Adjusted path
import TablaEmpleados from '@/components/empleados/tablaEmpleados.vue' // Adjusted path
// Define the structure of the employee object
interface Employee {
id: string | number
name: string
cedula: number
avatar_url?: string
telefono?: string
ubicacion: string
idciat?: string
grupo_estudio?: string
empleado: boolean // To ensure we are dealing with employees
}
const router = useRouter()
const currentView = ref<'card' | 'table'>('card') // Default view
const employees = ref<Employee[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
// Mock data for employees - replace with actual API call
const mockEmployees: Employee[] = [
{
id: '1',
name: 'Ana García',
cedula: 123456789,
avatar_url: 'https://randomuser.me/api/portraits/women/60.jpg',
telefono: '0991234567',
ubicacion: 'Oficina Central',
idciat: 'AG001',
grupo_estudio: 'Desarrollo Frontend',
empleado: true,
},
{
id: '2',
name: 'Carlos Rodriguez',
cedula: 987654321,
avatar_url: 'https://randomuser.me/api/portraits/men/45.jpg',
telefono: '0987654321',
ubicacion: 'Sucursal Norte',
idciat: 'CR002',
grupo_estudio: 'Backend Services',
empleado: true,
},
{
id: '3',
name: 'Luisa Martinez',
cedula: 112233445,
// avatar_url: '', // Test fallback avatar
telefono: '0976543210',
ubicacion: 'Remoto',
idciat: 'LM003',
// grupo_estudio: '', // Test missing optional field
empleado: true,
},
{
id: '4',
name: 'Jorge Herrera',
cedula: 223344556,
avatar_url: 'https://randomuser.me/api/portraits/men/50.jpg',
telefono: '0965432109',
ubicacion: 'Oficina Central',
idciat: 'JH004',
grupo_estudio: 'QA Team',
empleado: true,
},
];
const fetchEmployees = async () => {
loading.value = true
error.value = null
try {
// Simulate API call delay
await new Promise(resolve => setTimeout(resolve, 1000));
// In a real application, you would fetch from your API:
// const response = await fetch('/api/clientes?empleado=true');
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// const data = await response.json();
// employees.value = data.filter((cliente: Employee) => cliente.empleado);
// Using mock data for now
employees.value = mockEmployees.filter(e => e.empleado);
} catch (e) {
console.error("Failed to fetch employees:", e);
if (e instanceof Error) {
error.value = e.message;
} else {
error.value = "Ocurrió un error desconocido."
}
employees.value = []; // Clear employees on error or set to empty if preferred
} finally {
loading.value = false
}
}
onMounted(() => {
fetchEmployees()
})
const goToCreateEmployee = () => {
router.push({ name: 'empleados-new' });
}
</script>
<style scoped>
/* Additional styling for the index page */
.min-h-screen {
min-height: calc(100vh - var(--navbar-height, 0px)); /* Adjust if you have a fixed navbar */
}
/* Styling for active/inactive toggle buttons */
button.focus\:ring-blue-400:focus { /* More specific focus for blue buttons */
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.5); /* Tailwind's blue-400 with 50% opacity */
}
button.focus\:ring-gray-400:focus { /* More specific focus for gray buttons */
box-shadow: 0 0 0 3px rgba(156, 163, 175, 0.5); /* Tailwind's gray-400 with 50% opacity */
}
/* Transition for views, if desired (can be more complex) */
.view-enter-active, .view-leave-active {
transition: opacity 0.3s ease;
}
.view-enter-from, .view-leave-to {
opacity: 0;
}
</style>