Feat: Implement card view option in Asistencias, Planillas, and Tareas modules

This commit introduces a selectable card view in the index pages for the Asistencias, Planillas, and Tareas modules, similar to the existing functionality in the Empleados module.

Key changes:
- Added view toggle buttons (Table/Cards) to `AsistenciasIndex.vue`, `PlanillasIndex.vue`, and `TareasIndex.vue`.
- Implemented conditional rendering to display either the existing data table or a new grid of card components.
- Ensured card components (`cardAsistencia.vue`, `cardPlanilla.vue`, `cardTarea.vue`) are correctly populated with data from their respective Pinia stores.
- Styled the active view toggle button using module-specific accent colors.
- Maintained loading and error state displays for both views.
- Updated "no data" messages to be specific to table or card view.

These enhancements provide you with an alternative way to visualize module data, improving the overall user experience.
This commit is contained in:
google-labs-jules[bot]
2025-05-31 06:32:19 +00:00
parent ac939341a0
commit 3dabcad617
6 changed files with 185 additions and 40 deletions

View File

@@ -1,12 +1,13 @@
<template>
<div class="asistencia-card">
<div class="card-header">
<span class="empleado-id">Empleado ID: {{ asistencia.empleado_id }}</span>
<h4>Asistencia ID: {{ asistencia.id }}</h4>
<span :class="`estado-asistencia estado-${asistencia.estado?.toLowerCase().replace(/\s+/g, '-')}`">
{{ asistencia.estado || 'N/A' }}
</span>
</div>
<div class="card-body">
<p><strong>Empleado ID:</strong> {{ asistencia.empleado_id }}</p>
<p><strong>Entrada:</strong> {{ formatDateTime(asistencia.entrada) }}</p>
<p><strong>Salida:</strong> {{ asistencia.salida ? formatDateTime(asistencia.salida) : 'No registrada' }}</p>
<p v-if="asistencia.observacion" class="observacion">
@@ -40,14 +41,14 @@ const asistenciasStore = useAsistenciasStore();
const formatDateTime = (dateTimeString) => {
if (!dateTimeString) return 'N/A';
const date = new Date(dateTimeString);
return date.toLocaleString('es-ES', { // Spanish locale
year: 'numeric',
month: '2-digit', // Using 2-digit for month for brevity in card
day: '2-digit', // Using 2-digit for day
hour: '2-digit',
minute: '2-digit',
return date.toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); // Spanish locale
// year: 'numeric', // Already present in the new options
// month: '2-digit', // Using 2-digit for month for brevity in card // Already present in the new options
// day: '2-digit', // Using 2-digit for day // Already present in the new options
// hour: '2-digit', // Already present in the new options
// minute: '2-digit', // Already present in the new options
// second: '2-digit', // Optional: include seconds
});
// }); // Removed this line as options are now in a single line
};
const editAsistencia = () => {
@@ -96,7 +97,14 @@ const deleteAsistenciaInternal = async () => {
border-bottom: 1px solid #f0f0f0; /* Even lighter border */
}
.empleado-id {
.card-header h4 { /* Style for the new Asistencia ID header */
font-weight: bold;
color: var(--accent-color-asistencias); /* Accent color */
font-size: 1.15em; /* Slightly larger for main header element */
margin: 0; /* Remove default margin */
}
.empleado-id { /* This class is no longer used in the header, but could be reused elsewhere if needed */
font-weight: bold;
color: var(--accent-color-asistencias); /* Accent color */
font-size: 1.05em;

View File

@@ -1,6 +1,7 @@
<template>
<div class="planilla-card">
<h3>{{ planilla.titulo }}</h3>
<h4>Planilla ID: {{ planilla.id }}</h4>
<p><strong>Título:</strong> {{ planilla.titulo }}</p>
<p><strong>Empleado ID:</strong> {{ planilla.empleado_id }}</p>
<p><strong>Desde:</strong> {{ formatDate(planilla.fecha_desde) }}</p>
<p><strong>Hasta:</strong> {{ formatDate(planilla.fecha_hasta) }}</p>
@@ -31,11 +32,7 @@ const planillasStore = usePlanillasStore();
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
const date = new Date(dateString);
return date.toLocaleDateString('es-ES', { // Using Spanish locale for date
year: 'numeric',
month: 'long',
day: 'numeric',
});
return date.toLocaleDateString('es-HN', { timeZone: 'America/Tegucigalpa', year: 'numeric', month: 'long', day: 'numeric' });
};
const formatCurrency = (value) => {
@@ -80,7 +77,13 @@ const deletePlanilla = async () => {
background-color: #f9f9f9;
}
.planilla-card h3 {
.planilla-card h4 { /* Updated selector from h3 to h4 */
margin-top: 0;
color: var(--accent-color-planillas); /* Accent color for title */
font-size: 1.2em; /* Adjust size as needed, h4 is typically smaller than h3 */
}
.planilla-card h3 { /* Keep if h3 is used elsewhere, or remove if h4 is the new standard */
margin-top: 0;
color: var(--accent-color-planillas); /* Accent color for title */
}

View File

@@ -1,6 +1,7 @@
<template>
<div class="tarea-card">
<h4>{{ tarea.titulo }}</h4>
<h4>Tarea ID: {{ tarea.id }}</h4>
<p><strong>Título:</strong> {{ tarea.titulo }}</p>
<p><strong>Empleado ID:</strong> {{ tarea.empleado_id }}</p>
<p><strong>Fecha:</strong> {{ formatDate(tarea.fecha) }}</p>
<p><strong>Estado:</strong> <span :class="`estado-${tarea.estado?.toLowerCase().replace(/\s+/g, '-')}`">{{ tarea.estado }}</span></p>
@@ -33,11 +34,7 @@ const tareasStore = useTareasStore();
const formatDate = (dateString) => {
if (!dateString) return 'N/A';
const date = new Date(dateString);
return date.toLocaleDateString('es-ES', { // Spanish locale
year: 'numeric',
month: 'long',
day: 'numeric',
});
return date.toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' });
};
const formatCurrency = (value) => {

View File

@@ -7,6 +7,16 @@
</button>
</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="btnClass('card')">
Tarjetas
</button>
<button @click="currentView = 'table'" :class="btnClass('table')">
Tabla
</button>
</div>
<div v-if="isLoading" class="loading-message">
Cargando asistencias...
</div>
@@ -17,12 +27,30 @@
</div>
<div v-else>
<tabla-asistencias
:asistencias="asistenciasList"
@edit="handleEditAsistencia"
/>
<div v-if="!asistenciasList || asistenciasList.length === 0 && !isLoading" class="no-data-message">
No hay asistencias registradas. Puede registrar una nueva haciendo clic en el botón de arriba.
<!-- Vista de Tabla -->
<div v-if="currentView === 'table'">
<tabla-asistencias
:asistencias="asistenciasList"
@edit="handleEditAsistencia"
/>
<div v-if="asistenciasList.length === 0 && !isLoading" class="no-data-message">
No hay asistencias para mostrar en la vista de tabla.
</div>
</div>
<!-- Vista de Tarjetas -->
<div v-if="currentView === 'card'">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<CardAsistencia
v-for="asistencia in asistenciasList"
:key="asistencia.id"
:asistencia="asistencia"
@edit="handleEditAsistencia"
/>
</div>
<div v-if="asistenciasList.length === 0 && !isLoading" class="no-data-message">
No hay asistencias para mostrar en la vista de tarjetas.
</div>
</div>
</div>
@@ -34,6 +62,7 @@ import { ref, computed, onMounted } from 'vue';
import { useAsistenciasStore } from '../../stores/useAsistencias';
import { useRouter } from 'vue-router';
import TablaAsistencias from '../../components/asistencias/tablaAsistencias.vue';
import CardAsistencia from '../../components/asistencias/cardAsistencia.vue';
const asistenciasStore = useAsistenciasStore();
const router = useRouter();
@@ -42,6 +71,8 @@ const isLoading = ref(true);
const errorLoading = ref(false);
const errorMessage = ref('');
const currentView = ref('table'); // Default to table view
const asistenciasList = computed(() => asistenciasStore.asistencias);
onMounted(async () => {
@@ -69,6 +100,14 @@ const handleEditAsistencia = (asistenciaId) => {
router.push({ name: 'asistencias-edit', params: { id: asistenciaId } });
};
const btnClass = (view) => {
const baseClasses = 'px-4 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2';
if (currentView.value === view) {
return `${baseClasses} text-white shadow-sm view-toggle-active-asistencias`;
}
return `${baseClasses} bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-400`;
};
</script>
<style scoped>
@@ -140,4 +179,12 @@ const handleEditAsistencia = (asistenciaId) => {
color: #343a40;
border: 1px solid #e9ecef;
}
/* Added for view toggle buttons */
.view-toggle-active-asistencias {
background-color: var(--accent-color-asistencias);
/* Assuming --background-color is defined globally or use a fallback like #fff */
/* For Tailwind, theme('colors.white') could be an option if JIT is enabled and configured */
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-asistencias);
}
</style>

View File

@@ -7,6 +7,16 @@
</button>
</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="btnClass('card')">
Tarjetas
</button>
<button @click="currentView = 'table'" :class="btnClass('table')">
Tabla
</button>
</div>
<div v-if="isLoading" class="loading-message">
Cargando planillas...
</div>
@@ -17,12 +27,30 @@
</div>
<div v-else>
<tabla-planillas
:planillas="planillasList"
@edit="handleEditPlanilla"
/>
<div v-if="!planillasList || planillasList.length === 0 && !isLoading" class="no-data-message">
No hay planillas registradas. Puede crear una nueva haciendo clic en el botón de arriba.
<!-- Vista de Tabla -->
<div v-if="currentView === 'table'">
<tabla-planillas
:planillas="planillasList"
@edit="handleEditPlanilla"
/>
<div v-if="planillasList.length === 0 && !isLoading" class="no-data-message">
No hay planillas para mostrar en la vista de tabla.
</div>
</div>
<!-- Vista de Tarjetas -->
<div v-if="currentView === 'card'">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<CardPlanilla
v-for="planilla in planillasList"
:key="planilla.id"
:planilla="planilla"
@edit="handleEditPlanilla"
/>
</div>
<div v-if="planillasList.length === 0 && !isLoading" class="no-data-message">
No hay planillas para mostrar en la vista de tarjetas.
</div>
</div>
</div>
@@ -34,6 +62,7 @@ import { ref, computed, onMounted } from 'vue';
import { usePlanillasStore } from '../../stores/usePlanillas';
import { useRouter } from 'vue-router';
import TablaPlanillas from '../../components/planillas/tablaPlanillas.vue'; // Corrected path
import CardPlanilla from '../../components/planillas/cardPlanilla.vue';
const planillasStore = usePlanillasStore();
const router = useRouter();
@@ -42,6 +71,8 @@ const isLoading = ref(true); // Set to true initially
const errorLoading = ref(false);
const errorMessage = ref('');
const currentView = ref('table'); // Default to table view
// Computed property to get planillas from the store
const planillasList = computed(() => planillasStore.planillas);
@@ -75,6 +106,14 @@ const handleEditPlanilla = (planillaId) => {
router.push({ name: 'planillas-edit', params: { id: planillaId } });
};
const btnClass = (view) => {
const baseClasses = 'px-4 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2';
if (currentView.value === view) {
return `${baseClasses} text-white shadow-sm view-toggle-active-planillas`;
}
return `${baseClasses} bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-400`;
};
</script>
<style scoped>
@@ -149,4 +188,10 @@ const handleEditPlanilla = (planillaId) => {
/* Styling for the imported tablaPlanillas can be managed within its own component,
but you can add overrides or container styles here if needed. */
/* Added for view toggle buttons */
.view-toggle-active-planillas {
background-color: var(--accent-color-planillas);
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-planillas);
}
</style>

View File

@@ -7,6 +7,16 @@
</button>
</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="btnClass('card')">
Tarjetas
</button>
<button @click="currentView = 'table'" :class="btnClass('table')">
Tabla
</button>
</div>
<div v-if="isLoading" class="loading-message">
Cargando tareas...
</div>
@@ -17,12 +27,30 @@
</div>
<div v-else>
<tabla-tareas
:tareas="tareasList"
@edit="handleEditTarea"
/>
<div v-if="!tareasList || tareasList.length === 0 && !isLoading" class="no-data-message">
No hay tareas registradas. Puede crear una nueva haciendo clic en el botón de arriba.
<!-- Vista de Tabla -->
<div v-if="currentView === 'table'">
<tabla-tareas
:tareas="tareasList"
@edit="handleEditTarea"
/>
<div v-if="tareasList.length === 0 && !isLoading" class="no-data-message">
No hay tareas para mostrar en la vista de tabla.
</div>
</div>
<!-- Vista de Tarjetas -->
<div v-if="currentView === 'card'">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<CardTarea
v-for="tarea in tareasList"
:key="tarea.id"
:tarea="tarea"
@edit="handleEditTarea"
/>
</div>
<div v-if="tareasList.length === 0 && !isLoading" class="no-data-message">
No hay tareas para mostrar en la vista de tarjetas.
</div>
</div>
</div>
@@ -34,6 +62,7 @@ import { ref, computed, onMounted } from 'vue';
import { useTareasStore } from '../../stores/useTareas';
import { useRouter } from 'vue-router';
import TablaTareas from '../../components/tareas/tablaTareas.vue';
import CardTarea from '../../components/tareas/cardTarea.vue';
const tareasStore = useTareasStore();
const router = useRouter();
@@ -42,6 +71,8 @@ const isLoading = ref(true);
const errorLoading = ref(false);
const errorMessage = ref('');
const currentView = ref('table'); // Default to table view
const tareasList = computed(() => tareasStore.tareas);
onMounted(async () => {
@@ -69,6 +100,14 @@ const handleEditTarea = (tareaId) => {
router.push({ name: 'tareas-edit', params: { id: tareaId } });
};
const btnClass = (view) => {
const baseClasses = 'px-4 py-2 rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2';
if (currentView.value === view) {
return `${baseClasses} text-white shadow-sm view-toggle-active-tareas`;
}
return `${baseClasses} bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-400`;
};
</script>
<style scoped>
@@ -140,4 +179,10 @@ const handleEditTarea = (tareaId) => {
color: #495057;
border: 1px solid #ced4da;
}
/* Added for view toggle buttons */
.view-toggle-active-tareas {
background-color: var(--accent-color-tareas);
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-tareas);
}
</style>