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:
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user