diff --git a/ui/src/components/asistencias/cardAsistencia.vue b/ui/src/components/asistencias/cardAsistencia.vue index b2b3757..6eaeed8 100644 --- a/ui/src/components/asistencias/cardAsistencia.vue +++ b/ui/src/components/asistencias/cardAsistencia.vue @@ -1,2 +1,181 @@ + - \ No newline at end of file + + + diff --git a/ui/src/components/asistencias/tablaAsistencias.vue b/ui/src/components/asistencias/tablaAsistencias.vue index b2b3757..ecdc0e7 100644 --- a/ui/src/components/asistencias/tablaAsistencias.vue +++ b/ui/src/components/asistencias/tablaAsistencias.vue @@ -1,2 +1,163 @@ + - \ No newline at end of file + +.tabla-asistencias-container { + overflow-x: auto; +} + +.tabla-asistencias { + width: 100%; + border-collapse: collapse; + margin-top: 1em; + font-size: 0.9em; +} + +.tabla-asistencias th, +.tabla-asistencias td { + border: 1px solid #ddd; + padding: 10px; + text-align: left; + vertical-align: middle; /* Good for table cells */ +} + +.tabla-asistencias th { + background-color: #f4f6f8; /* Light grey for header */ + font-weight: 600; /* Bolder text for header */ + color: #333; +} + +.tabla-asistencias tr:nth-child(even) { + background-color: #f9fafb; /* Very light alternating row color */ +} + +.tabla-asistencias tr:hover { + background-color: #f0f0f0; /* Hover effect */ +} + +.action-button { + padding: 6px 10px; + margin-right: 6px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.85em; + transition: background-color 0.2s ease, transform 0.1s ease; +} +.action-button:hover { + transform: translateY(-1px); /* Slight lift effect */ +} + +.edit-button { + background-color: #007bff; /* Blue */ + color: white; +} +.edit-button:hover { + background-color: #0056b3; +} + +.delete-button { + background-color: #dc3545; /* Red */ + color: white; +} +.delete-button:hover { + background-color: #c82333; +} + +/* Estado specific styling (using text color for tables is often cleaner) */ +.estado-pendiente { color: #ffc107; font-weight: bold; } /* Amber */ +.estado-presente, +.estado-confirmada { color: #28a745; font-weight: bold; } /* Green */ +.estado-ausente { color: #dc3545; font-weight: bold; } /* Red */ +.estado-justificada { color: #17a2b8; font-weight: bold; } /* Info Blue */ +.estado-cancelada, +.estado-anulada { color: #6c757d; font-weight: bold; } /* Gray */ +/* If you prefer background colors like in cards, copy those styles here, but they can be visually heavy in tables. */ + diff --git a/ui/src/components/planillas/cardPlanilla.vue b/ui/src/components/planillas/cardPlanilla.vue index b2b3757..55a6d59 100644 --- a/ui/src/components/planillas/cardPlanilla.vue +++ b/ui/src/components/planillas/cardPlanilla.vue @@ -1,2 +1,134 @@ + - \ No newline at end of file + + + diff --git a/ui/src/components/planillas/tablaPlanillas.vue b/ui/src/components/planillas/tablaPlanillas.vue index b2b3757..d17d8d6 100644 --- a/ui/src/components/planillas/tablaPlanillas.vue +++ b/ui/src/components/planillas/tablaPlanillas.vue @@ -1,2 +1,167 @@ + - \ No newline at end of file + + + diff --git a/ui/src/components/tareas/cardTarea.vue b/ui/src/components/tareas/cardTarea.vue index b2b3757..ed855ec 100644 --- a/ui/src/components/tareas/cardTarea.vue +++ b/ui/src/components/tareas/cardTarea.vue @@ -1,2 +1,159 @@ + - \ No newline at end of file + + + diff --git a/ui/src/components/tareas/tablaTareas.vue b/ui/src/components/tareas/tablaTareas.vue index b2b3757..c25b903 100644 --- a/ui/src/components/tareas/tablaTareas.vue +++ b/ui/src/components/tareas/tablaTareas.vue @@ -1,2 +1,167 @@ + - \ No newline at end of file + + + diff --git a/ui/src/stores/useAsistencias.js b/ui/src/stores/useAsistencias.js index 45a6c1b..d89883f 100644 --- a/ui/src/stores/useAsistencias.js +++ b/ui/src/stores/useAsistencias.js @@ -1,5 +1,88 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; +import apiClient from '../apiClient'; // Assuming apiClient is configured -export const useAsistencias = defineStore('asistencias', { - state: () => ({ asistencias: [] }), -}) +// Helper function to get default values for currentAsistencia +const getDefaultCurrentAsistencia = () => ({ + id: null, + empleado_id: null, + entrada: null, // Will likely be a datetime string + salida: null, // Will likely be a datetime string + historial: null, // JSON field, can be an object or string + observacion: '', + estado: 'pendiente', // Default from schema (if applicable, or common practice) + // fecha_anulado is not typically part of creation/edit form directly +}); + +export const useAsistenciasStore = defineStore('asistencias', { + state: () => ({ + asistencias: [], + currentAsistencia: getDefaultCurrentAsistencia(), + }), + actions: { + async fetchAsistencias() { + try { + const response = await apiClient.get('/api/asistencias'); + this.asistencias = response.data; + } catch (error) { + console.error('Error fetching asistencias:', error); + // Handle error (e.g., show a notification to the user) + } + }, + + async fetchAsistenciaById(id) { + try { + const response = await apiClient.get(`/api/asistencias/${id}`); + // Assuming API returns dates as ISO strings. + // No special date conversion needed here, Pinia will store as is. + // Components will handle formatting for display or input fields. + this.currentAsistencia = response.data; + } catch (error) { + console.error(`Error fetching asistencia with id ${id}:`, error); + this.currentAsistencia = getDefaultCurrentAsistencia(); // Reset on error + } + }, + + async createAsistencia(asistenciaData) { + try { + // Ensure date fields are in a format the API expects (e.g., ISO string) + // If asistenciaData.entrada/salida are Date objects, convert them: + // if (asistenciaData.entrada instanceof Date) asistenciaData.entrada = asistenciaData.entrada.toISOString(); + // if (asistenciaData.salida instanceof Date) asistenciaData.salida = asistenciaData.salida.toISOString(); + await apiClient.post('/api/asistencias', asistenciaData); + await this.fetchAsistencias(); // Refresh the list + } catch (error) { + console.error('Error creating asistencia:', error); + throw error; // Re-throw to allow form to handle it + } + }, + + async updateAsistencia(id, asistenciaData) { + try { + // Similar date conversion logic as in createAsistencia if needed + // if (asistenciaData.entrada instanceof Date) asistenciaData.entrada = asistenciaData.entrada.toISOString(); + // if (asistenciaData.salida instanceof Date) asistenciaData.salida = asistenciaData.salida.toISOString(); + await apiClient.put(`/api/asistencias/${id}`, asistenciaData); + await this.fetchAsistencias(); // Refresh the list + this.currentAsistencia = getDefaultCurrentAsistencia(); // Reset currentAsistencia + } catch (error) { + console.error(`Error updating asistencia with id ${id}:`, error); + throw error; // Re-throw to allow form to handle it + } + }, + + async deleteAsistencia(id) { + try { + await apiClient.delete(`/api/asistencias/${id}`); + await this.fetchAsistencias(); // Refresh the list + } catch (error) { + console.error(`Error deleting asistencia with id ${id}:`, error); + throw error; // Re-throw to allow handling in component + } + }, + + // Action to clear currentAsistencia, useful when navigating away from a form + clearCurrentAsistencia() { + this.currentAsistencia = getDefaultCurrentAsistencia(); + } + }, +}); diff --git a/ui/src/stores/usePlanillas.js b/ui/src/stores/usePlanillas.js index 5a264f7..3025e0e 100644 --- a/ui/src/stores/usePlanillas.js +++ b/ui/src/stores/usePlanillas.js @@ -1,5 +1,81 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; +import apiClient from '../apiClient'; // Assuming apiClient is configured for API calls -export const usePlanillas = defineStore('planillas', { - state: () => ({ planillas: [] }), -}) +export const usePlanillasStore = defineStore('planillas', { + state: () => ({ + planillas: [], + currentPlanilla: { + id: null, + fecha_desde: null, + fecha_hasta: null, + titulo: '', + total: null, + estado: 'pagado', // Default value from schema + fecha_anulado: null, + empleado_id: null, + // Empleado relation is not included here, will be handled by ID. + }, + }), + actions: { + async fetchPlanillas() { + try { + const response = await apiClient.get('/api/planillas'); + this.planillas = response.data; + } catch (error) { + console.error('Error fetching planillas:', error); + // Handle error (e.g., show a notification to the user) + } + }, + + async fetchPlanillaById(id) { + try { + const response = await apiClient.get(`/api/planillas/${id}`); + this.currentPlanilla = response.data; + } catch (error) { + console.error(`Error fetching planilla with id ${id}:`, error); + // Handle error + } + }, + + async createPlanilla(planillaData) { + try { + await apiClient.post('/api/planillas', planillaData); + await this.fetchPlanillas(); // Refresh the list + } catch (error) { + console.error('Error creating planilla:', error); + // Handle error + } + }, + + async updatePlanilla(id, planillaData) { + try { + await apiClient.put(`/api/planillas/${id}`, planillaData); + await this.fetchPlanillas(); // Refresh the list + this.currentPlanilla = { // Reset currentPlanilla + id: null, + fecha_desde: null, + fecha_hasta: null, + titulo: '', + total: null, + estado: 'pagado', + fecha_anulado: null, + empleado_id: null, + }; + } catch (error) { + console.error(`Error updating planilla with id ${id}:`, error); + // Handle error + } + }, + + async deletePlanilla(id) { + try { + await apiClient.delete(`/api/planillas/${id}`); + await this.fetchPlanillas(); // Refresh the list + } catch (error) + { + console.error(`Error deleting planilla with id ${id}:`, error); + // Handle error + } + }, + }, +}); diff --git a/ui/src/stores/useTareas.js b/ui/src/stores/useTareas.js index b8caafb..145088e 100644 --- a/ui/src/stores/useTareas.js +++ b/ui/src/stores/useTareas.js @@ -1,5 +1,80 @@ -import { defineStore } from 'pinia' +import { defineStore } from 'pinia'; +import apiClient from '../apiClient'; // Assuming apiClient is configured -export const useTareas = defineStore('tareas', { - state: () => ({ tareas: [] }), -}) +// Helper function to get default values for currentTarea +const getDefaultCurrentTarea = () => ({ + id: null, + empleado_id: null, + planilla_id: null, // Optional + titulo: '', + precio: null, // Optional + estado: 'pendiente', // Default from schema + observacion: '', // Optional + fecha: null, // Should be a date + tipo: '', // Default from schema + // fecha_anulado is not typically part of creation/edit form directly +}); + +export const useTareasStore = defineStore('tareas', { + state: () => ({ + tareas: [], + currentTarea: getDefaultCurrentTarea(), + }), + actions: { + async fetchTareas() { + try { + const response = await apiClient.get('/api/tareas'); + this.tareas = response.data; + } catch (error) { + console.error('Error fetching tareas:', error); + // Consider more sophisticated error handling (e.g., user notifications) + } + }, + + async fetchTareaById(id) { + try { + const response = await apiClient.get(`/api/tareas/${id}`); + this.currentTarea = response.data; + } catch (error) { + console.error(`Error fetching tarea with id ${id}:`, error); + this.currentTarea = getDefaultCurrentTarea(); // Reset on error + } + }, + + async createTarea(tareaData) { + try { + await apiClient.post('/api/tareas', tareaData); + await this.fetchTareas(); // Refresh the list + } catch (error) { + console.error('Error creating tarea:', error); + throw error; // Re-throw to allow form to handle it + } + }, + + async updateTarea(id, tareaData) { + try { + await apiClient.put(`/api/tareas/${id}`, tareaData); + await this.fetchTareas(); // Refresh the list + this.currentTarea = getDefaultCurrentTarea(); // Reset currentTarea + } catch (error) { + console.error(`Error updating tarea with id ${id}:`, error); + throw error; // Re-throw to allow form to handle it + } + }, + + async deleteTarea(id) { + try { + await apiClient.delete(`/api/tareas/${id}`); + await this.fetchTareas(); // Refresh the list + } catch (error) { + console.error(`Error deleting tarea with id ${id}:`, error); + throw error; // Re-throw to allow handling in component + } + }, + + // Action to clear currentTarea, useful when navigating away from a form + clearCurrentTarea() { + this.currentTarea = getDefaultCurrentTarea(); + } + }, +}); diff --git a/ui/src/views/asistencias/AsistenciaForm.vue b/ui/src/views/asistencias/AsistenciaForm.vue index b2b3757..e0ee703 100644 --- a/ui/src/views/asistencias/AsistenciaForm.vue +++ b/ui/src/views/asistencias/AsistenciaForm.vue @@ -1,2 +1,297 @@ + + + + + diff --git a/ui/src/views/asistencias/AsistenciasIndex.vue b/ui/src/views/asistencias/AsistenciasIndex.vue index b2b3757..730449c 100644 --- a/ui/src/views/asistencias/AsistenciasIndex.vue +++ b/ui/src/views/asistencias/AsistenciasIndex.vue @@ -1,2 +1,143 @@ + + + + + diff --git a/ui/src/views/planillas/PlanillaForm.vue b/ui/src/views/planillas/PlanillaForm.vue index b2b3757..98aa082 100644 --- a/ui/src/views/planillas/PlanillaForm.vue +++ b/ui/src/views/planillas/PlanillaForm.vue @@ -1,2 +1,305 @@ + + + + + diff --git a/ui/src/views/planillas/PlanillasIndex.vue b/ui/src/views/planillas/PlanillasIndex.vue index b2b3757..55eefc5 100644 --- a/ui/src/views/planillas/PlanillasIndex.vue +++ b/ui/src/views/planillas/PlanillasIndex.vue @@ -1,2 +1,152 @@ + + + + + diff --git a/ui/src/views/tareas/TareaForm.vue b/ui/src/views/tareas/TareaForm.vue index b2b3757..094d6a3 100644 --- a/ui/src/views/tareas/TareaForm.vue +++ b/ui/src/views/tareas/TareaForm.vue @@ -1,2 +1,318 @@ + + + + + diff --git a/ui/src/views/tareas/TareasIndex.vue b/ui/src/views/tareas/TareasIndex.vue index b2b3757..6cfe75d 100644 --- a/ui/src/views/tareas/TareasIndex.vue +++ b/ui/src/views/tareas/TareasIndex.vue @@ -1,2 +1,143 @@ + + + + +