All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m0s
- Crear 5 queries SQL en Metabase para datos de empleados: * Contadores generales (horas, días, tareas) * Lista de empleados con totales agregados * Detalle de tareas realizadas * Detalle de asistencias con cálculo de horas * Opciones de filtros disponibles - Implementar backend API endpoint /api/metabase/informe-empleados * Soporte para filtros por fecha, empleados, títulos de tareas y planillas * Ejecución paralela de queries con manejo de errores * Transformación de datos de Metabase a formato consumible - Crear componente TotalesEmpleados.vue * Visualización de métricas principales (horas, días, tareas) * Cálculo de promedios por empleado * Funcionalidad de copiar texto/JSON - Implementar página informe-empleados.vue * Layout tipo informe con selector de fechas * Filtros avanzados por empleado, títulos de tareas y planillas * Tabla integrada de empleados con métricas clave * Estados de carga, error y bienvenida * Detección de cambios pendientes - Actualizar configuración de queries en metabase-queries.ts Estructura trabajada: - clientes (empleado = true) - asistencias (con cálculo de horas trabajadas) - tareas_realizadas (con títulos y planillas) - planillas (con totales y rangos de fechas)
184 lines
5.9 KiB
TypeScript
184 lines
5.9 KiB
TypeScript
import { METABASE_QUERIES } from '../../config/metabase-queries'
|
|
|
|
/**
|
|
* Execute all informe empleados queries in parallel
|
|
* Returns data for the Informe de Empleados page
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const body = await readBody(event)
|
|
|
|
const {
|
|
fecha_desde = null,
|
|
fecha_hasta = null,
|
|
empleado_ids = [],
|
|
titulos_tareas = [],
|
|
titulos_planillas = []
|
|
} = body
|
|
|
|
try {
|
|
// First, get all cards to find our informe empleados queries
|
|
const allCards = await getMetabaseCards('all')
|
|
|
|
// Find our informe empleados queries by name using centralized config
|
|
const queryNames = METABASE_QUERIES.informe_empleados
|
|
|
|
const cards: Record<string, any> = {}
|
|
|
|
for (const [key, name] of Object.entries(queryNames)) {
|
|
const card = allCards.find((c: any) => c.name === name)
|
|
if (!card) {
|
|
console.warn(`[Informe Empleados] Query not found: ${name}`)
|
|
} else {
|
|
cards[key] = card
|
|
}
|
|
}
|
|
|
|
// Build parameters array for Metabase queries
|
|
const buildParameters = () => {
|
|
const params = [
|
|
{
|
|
type: 'text',
|
|
target: ['variable', ['template-tag', 'fecha_desde']],
|
|
value: fecha_desde || ''
|
|
},
|
|
{
|
|
type: 'text',
|
|
target: ['variable', ['template-tag', 'fecha_hasta']],
|
|
value: fecha_hasta || ''
|
|
}
|
|
]
|
|
|
|
// Solo agregar filtros opcionales si tienen valores (no vacíos)
|
|
if (empleado_ids && Array.isArray(empleado_ids) && empleado_ids.length > 0) {
|
|
params.push({
|
|
type: 'number',
|
|
target: ['variable', ['template-tag', 'empleado_ids']],
|
|
value: empleado_ids
|
|
})
|
|
}
|
|
|
|
if (titulos_tareas && Array.isArray(titulos_tareas) && titulos_tareas.length > 0) {
|
|
params.push({
|
|
type: 'text',
|
|
target: ['variable', ['template-tag', 'titulos_tareas']],
|
|
value: titulos_tareas
|
|
})
|
|
}
|
|
|
|
if (titulos_planillas && Array.isArray(titulos_planillas) && titulos_planillas.length > 0) {
|
|
params.push({
|
|
type: 'text',
|
|
target: ['variable', ['template-tag', 'titulos_planillas']],
|
|
value: titulos_planillas
|
|
})
|
|
}
|
|
|
|
return params
|
|
}
|
|
|
|
const standardParams = buildParameters()
|
|
const emptyParams: any[] = [] // Para opciones_filtros que no requiere parámetros
|
|
|
|
// Execute all queries in parallel with error handling
|
|
const executeWithErrorHandling = async (name: string, cardId: number | undefined, parameters: any[], defaultValue: any) => {
|
|
if (!cardId) {
|
|
console.warn(`[Informe Empleados] No card ID for ${name}`)
|
|
return defaultValue
|
|
}
|
|
|
|
try {
|
|
console.log(`[Informe Empleados] Executing query: ${name} (ID: ${cardId})`)
|
|
const result = await executeCardQuery(cardId, parameters)
|
|
console.log(`[Informe Empleados] Query ${name} returned ${result.data?.rows?.length || 0} rows`)
|
|
return result
|
|
} catch (error: any) {
|
|
console.error(`[Informe Empleados] Error executing ${name}:`, error.message)
|
|
return defaultValue
|
|
}
|
|
}
|
|
|
|
const [
|
|
contadores,
|
|
listaEmpleados,
|
|
detalleTareas,
|
|
detalleAsistencias,
|
|
opcionesFiltros
|
|
] = await Promise.all([
|
|
executeWithErrorHandling('contadores', cards.contadores?.id, standardParams, { data: { rows: [[]], cols: [] } }),
|
|
executeWithErrorHandling('lista_empleados', cards.lista_empleados?.id, standardParams, { data: { rows: [], cols: [] } }),
|
|
executeWithErrorHandling('detalle_tareas', cards.detalle_tareas?.id, standardParams, { data: { rows: [], cols: [] } }),
|
|
executeWithErrorHandling('detalle_asistencias', cards.detalle_asistencias?.id, standardParams, { data: { rows: [], cols: [] } }),
|
|
executeWithErrorHandling('opciones_filtros', cards.opciones_filtros?.id, emptyParams, { data: { rows: [], cols: [] } })
|
|
])
|
|
|
|
// Transform Metabase responses to objects for easier frontend consumption
|
|
const transformSingleRow = (result: any) => {
|
|
if (!result.data?.rows?.[0] || !result.data?.cols) return {}
|
|
|
|
const row = result.data.rows[0]
|
|
const cols = result.data.cols
|
|
const obj: any = {}
|
|
|
|
cols.forEach((col: any, index: number) => {
|
|
obj[col.name] = row[index]
|
|
})
|
|
|
|
return obj
|
|
}
|
|
|
|
const transformMultipleRows = (result: any) => {
|
|
if (!result.data?.rows || !result.data?.cols) return []
|
|
|
|
const cols = result.data.cols
|
|
return result.data.rows.map((row: any[]) => {
|
|
const obj: any = {}
|
|
cols.forEach((col: any, index: number) => {
|
|
obj[col.name] = row[index]
|
|
})
|
|
return obj
|
|
})
|
|
}
|
|
|
|
// Transform opciones_filtros to a more usable format
|
|
const transformOpcionesFiltros = (result: any) => {
|
|
if (!result.data?.rows || !result.data?.cols) {
|
|
return {
|
|
titulos_tareas: [],
|
|
titulos_planillas: []
|
|
}
|
|
}
|
|
|
|
const rows = transformMultipleRows(result)
|
|
const opciones: any = {
|
|
titulos_tareas: [],
|
|
titulos_planillas: []
|
|
}
|
|
|
|
rows.forEach((row: any) => {
|
|
if (row.tipo_opcion === 'titulos_tareas') {
|
|
opciones.titulos_tareas.push(row.valor)
|
|
} else if (row.tipo_opcion === 'titulos_planillas') {
|
|
opciones.titulos_planillas.push(row.valor)
|
|
}
|
|
})
|
|
|
|
return opciones
|
|
}
|
|
|
|
// Return all data in a structured format
|
|
return {
|
|
contadores: transformSingleRow(contadores),
|
|
listaEmpleados: transformMultipleRows(listaEmpleados),
|
|
detalleTareas: transformMultipleRows(detalleTareas),
|
|
detalleAsistencias: transformMultipleRows(detalleAsistencias),
|
|
opcionesFiltros: transformOpcionesFiltros(opcionesFiltros)
|
|
}
|
|
} catch (error: any) {
|
|
console.error('[API] Failed to execute informe empleados queries:', error)
|
|
throw createError({
|
|
statusCode: error.statusCode || 500,
|
|
statusMessage: error.statusMessage || 'Failed to execute informe empleados queries'
|
|
})
|
|
}
|
|
})
|