Cargando asistencias...
@@ -95,7 +113,13 @@ const handleEditAsistencia = (asistenciaId) => {
router.push({ name: 'asistencias-edit', params: { id: asistenciaId } });
};
-// Removed btnClass as manual toggle buttons are removed
+const btnViewClass = (viewType) => {
+ const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
+ if (currentView.value === viewType) {
+ return `${base} bg-[var(--accent-color-asistencias)] text-white shadow-lg`;
+ }
+ return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
+};
diff --git a/ui/src/views/asistencias/__tests__/AsistenciasIndex.spec.js b/ui/src/views/asistencias/__tests__/AsistenciasIndex.spec.js
index 6854eb9..b04b9fa 100644
--- a/ui/src/views/asistencias/__tests__/AsistenciasIndex.spec.js
+++ b/ui/src/views/asistencias/__tests__/AsistenciasIndex.spec.js
@@ -132,4 +132,85 @@ describe('AsistenciasIndex.vue', () => {
expect(wrapper.text()).toContain('No hay asistencias para mostrar');
});
})
+
+ describe('Local View Toggle Buttons', () => {
+ it('renders toggle buttons and reflects initial view from store (table)', async () => {
+ uiStoreMock.defaultViewAsistencias = 'table';
+ const wrapper = mountComponent();
+ await mockFetchAsistencias();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(tableViewButton.exists()).toBe(true);
+ expect(cardViewButton.exists()).toBe(true);
+
+ // Check active class based on btnViewClass logic
+ // Active: bg-[var(--accent-color-asistencias)] text-white
+ // Inactive: bg-gray-200 text-gray-700
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-asistencias)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('renders toggle buttons and reflects initial view from store (card)', async () => {
+ uiStoreMock.defaultViewAsistencias = 'card';
+ const wrapper = mountComponent();
+ await mockFetchAsistencias();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-asistencias)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('switches to card view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewAsistencias = 'table';
+ asistenciasStoreMock.asistencias = [{ id: 1, empleado: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchAsistencias();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+
+ await cardViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'CardAsistencia' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'TablaAsistencias' }).exists()).toBe(false);
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-asistencias)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewAsistencias).not.toHaveBeenCalled();
+ });
+
+ it('switches back to table view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewAsistencias = 'card'; // Start with card view
+ asistenciasStoreMock.asistencias = [{ id: 1, empleado: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchAsistencias();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ // Initially card view is active
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-asistencias)]');
+
+
+ await tableViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'TablaAsistencias' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'CardAsistencia' }).exists()).toBe(false);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-asistencias)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewAsistencias).not.toHaveBeenCalled();
+ });
+ });
})
diff --git a/ui/src/views/empleados/EmpleadosIndex.vue b/ui/src/views/empleados/EmpleadosIndex.vue
index a7240fe..90ec10b 100644
--- a/ui/src/views/empleados/EmpleadosIndex.vue
+++ b/ui/src/views/empleados/EmpleadosIndex.vue
@@ -19,7 +19,24 @@
-
+
@@ -88,6 +105,13 @@ const employees = empleados;
// --- helpers ---
// Removed btnClass as manual toggle buttons are removed
+const btnViewClass = (viewType: 'card' | 'table') => {
+ const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
+ if (currentView.value === viewType) {
+ return `${base} bg-[var(--accent-color-empleados)] text-white shadow-lg`;
+ }
+ return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
+};
// --- fetch inicial ---
const fetchEmployees = async () => {
diff --git a/ui/src/views/empleados/__tests__/EmpleadosIndex.spec.js b/ui/src/views/empleados/__tests__/EmpleadosIndex.spec.js
index 0dc3202..496134a 100644
--- a/ui/src/views/empleados/__tests__/EmpleadosIndex.spec.js
+++ b/ui/src/views/empleados/__tests__/EmpleadosIndex.spec.js
@@ -132,4 +132,77 @@ describe('EmpleadosIndex.vue', () => {
expect(wrapper.text()).toContain('No hay empleados para mostrar en la vista de tarjetas.');
});
})
+
+ describe('Local View Toggle Buttons', () => {
+ it('renders toggle buttons and reflects initial view from store (table)', async () => {
+ uiStoreMock.defaultViewEmpleados = 'table';
+ const wrapper = mountComponent();
+ // Wait for loading and reactivity
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(tableViewButton.exists()).toBe(true);
+ expect(cardViewButton.exists()).toBe(true);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-empleados)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('renders toggle buttons and reflects initial view from store (card)', async () => {
+ uiStoreMock.defaultViewEmpleados = 'card';
+ const wrapper = mountComponent();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-empleados)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('switches to card view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewEmpleados = 'table';
+ empleadosStoreMock.empleados = [{ id: 1, nombre: 'Test' }];
+ const wrapper = mountComponent();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+
+ await cardViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'CardEmpleado' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'TablaEmpleados' }).exists()).toBe(false);
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-empleados)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewEmpleados).not.toHaveBeenCalled();
+ });
+
+ it('switches back to table view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewEmpleados = 'card'; // Start with card view
+ empleadosStoreMock.empleados = [{ id: 1, nombre: 'Test' }];
+ const wrapper = mountComponent();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-empleados)]');
+
+ await tableViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'TablaEmpleados' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'CardEmpleado' }).exists()).toBe(false);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-empleados)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewEmpleados).not.toHaveBeenCalled();
+ });
+ });
})
diff --git a/ui/src/views/planillas/PlanillasIndex.vue b/ui/src/views/planillas/PlanillasIndex.vue
index 8240899..9e76a88 100644
--- a/ui/src/views/planillas/PlanillasIndex.vue
+++ b/ui/src/views/planillas/PlanillasIndex.vue
@@ -7,7 +7,25 @@
-
+
+
Cargando planillas...
@@ -101,7 +119,13 @@ const handleEditPlanilla = (planillaId) => {
router.push({ name: 'planillas-edit', params: { id: planillaId } });
};
-// Removed btnClass as manual toggle buttons are removed
+const btnViewClass = (viewType) => {
+ const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
+ if (currentView.value === viewType) {
+ return `${base} bg-[var(--accent-color-planillas)] text-white shadow-lg`;
+ }
+ return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
+};
diff --git a/ui/src/views/planillas/__tests__/PlanillasIndex.spec.js b/ui/src/views/planillas/__tests__/PlanillasIndex.spec.js
index eea69b5..b168fbb 100644
--- a/ui/src/views/planillas/__tests__/PlanillasIndex.spec.js
+++ b/ui/src/views/planillas/__tests__/PlanillasIndex.spec.js
@@ -125,4 +125,79 @@ describe('PlanillasIndex.vue', () => {
expect(wrapper.text()).toContain('No hay planillas para mostrar');
});
})
+
+ describe('Local View Toggle Buttons', () => {
+ it('renders toggle buttons and reflects initial view from store (table)', async () => {
+ uiStoreMock.defaultViewPlanillas = 'table';
+ const wrapper = mountComponent();
+ await mockFetchPlanillas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(tableViewButton.exists()).toBe(true);
+ expect(cardViewButton.exists()).toBe(true);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-planillas)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('renders toggle buttons and reflects initial view from store (card)', async () => {
+ uiStoreMock.defaultViewPlanillas = 'card';
+ const wrapper = mountComponent();
+ await mockFetchPlanillas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-planillas)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('switches to card view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewPlanillas = 'table';
+ planillasStoreMock.planillas = [{ id: 1, periodo: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchPlanillas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+
+ await cardViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'CardPlanilla' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'TablaPlanillas' }).exists()).toBe(false);
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-planillas)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewPlanillas).not.toHaveBeenCalled();
+ });
+
+ it('switches back to table view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewPlanillas = 'card'; // Start with card view
+ planillasStoreMock.planillas = [{ id: 1, periodo: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchPlanillas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-planillas)]');
+
+ await tableViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'TablaPlanillas' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'CardPlanilla' }).exists()).toBe(false);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-planillas)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewPlanillas).not.toHaveBeenCalled();
+ });
+ });
})
diff --git a/ui/src/views/tareas/TareasIndex.vue b/ui/src/views/tareas/TareasIndex.vue
index c31127b..4ec5d98 100644
--- a/ui/src/views/tareas/TareasIndex.vue
+++ b/ui/src/views/tareas/TareasIndex.vue
@@ -7,7 +7,25 @@
-
+
+
Cargando tareas...
@@ -95,7 +113,13 @@ const handleEditTarea = (tareaId) => {
router.push({ name: 'tareas-edit', params: { id: tareaId } });
};
-// Removed btnClass as manual toggle buttons are removed
+const btnViewClass = (viewType) => {
+ const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
+ if (currentView.value === viewType) {
+ return `${base} bg-[var(--accent-color-tareas)] text-white shadow-lg`;
+ }
+ return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
+};
diff --git a/ui/src/views/tareas/__tests__/TareasIndex.spec.js b/ui/src/views/tareas/__tests__/TareasIndex.spec.js
index 27bd6e4..2ec8820 100644
--- a/ui/src/views/tareas/__tests__/TareasIndex.spec.js
+++ b/ui/src/views/tareas/__tests__/TareasIndex.spec.js
@@ -125,4 +125,79 @@ describe('TareasIndex.vue', () => {
expect(wrapper.text()).toContain('No hay tareas para mostrar');
});
})
+
+ describe('Local View Toggle Buttons', () => {
+ it('renders toggle buttons and reflects initial view from store (table)', async () => {
+ uiStoreMock.defaultViewTareas = 'table';
+ const wrapper = mountComponent();
+ await mockFetchTareas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(tableViewButton.exists()).toBe(true);
+ expect(cardViewButton.exists()).toBe(true);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-tareas)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('renders toggle buttons and reflects initial view from store (card)', async () => {
+ uiStoreMock.defaultViewTareas = 'card';
+ const wrapper = mountComponent();
+ await mockFetchTareas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-tareas)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ });
+
+ it('switches to card view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewTareas = 'table';
+ tareasStoreMock.tareas = [{ id: 1, titulo: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchTareas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+
+ await cardViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'CardTarea' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'TablaTareas' }).exists()).toBe(false);
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-tareas)]');
+ expect(tableViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewTareas).not.toHaveBeenCalled();
+ });
+
+ it('switches back to table view on button click and updates button styles, does not call global store action', async () => {
+ uiStoreMock.defaultViewTareas = 'card'; // Start with card view
+ tareasStoreMock.tareas = [{ id: 1, titulo: 'Test' }];
+ const wrapper = mountComponent();
+ await mockFetchTareas();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const cardViewButton = wrapper.find('button[aria-label="Card View"]');
+ const tableViewButton = wrapper.find('button[aria-label="Table View"]');
+ expect(cardViewButton.classes()).toContain('bg-[var(--accent-color-tareas)]');
+
+ await tableViewButton.trigger('click');
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.findComponent({ name: 'TablaTareas' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ name: 'CardTarea' }).exists()).toBe(false);
+ expect(tableViewButton.classes()).toContain('bg-[var(--accent-color-tareas)]');
+ expect(cardViewButton.classes()).toContain('bg-gray-200');
+ expect(mockSetDefaultViewTareas).not.toHaveBeenCalled();
+ });
+ });
})