feat: agregar página de debug para Metabase
- Crear componente MetabaseCardDisplay para mostrar detalles de queries - Crear componente MetabaseCardsTable para listar todas las queries - Crear página /metabase-debug con vistas de tabla, cards y queries Panorama - Agregar API routes para cards de Metabase (GET, POST, export) - Actualizar metabase.ts para soportar API Key authentication - Agregar configuración de Metabase API Key en nuxt.config.ts - Documentar todos los endpoints disponibles en METABASE_API_ENDPOINTS.md
This commit is contained in:
203
nuxt4-app/app/pages/metabase-debug.vue
Normal file
203
nuxt4-app/app/pages/metabase-debug.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold">Metabase Debug</h1>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-1">
|
||||
Herramienta de debugging para queries de Metabase
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<UButton
|
||||
@click="refreshCards"
|
||||
:loading="loading"
|
||||
icon="i-heroicons-arrow-path"
|
||||
color="primary"
|
||||
variant="soft"
|
||||
>
|
||||
Actualizar
|
||||
</UButton>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<UCard>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold text-primary">{{ cards.length }}</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">Total Cards</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold text-blue-600">{{ nativeQueries }}</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">SQL Nativo</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold text-green-600">{{ queryBuilderQueries }}</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">Query Builder</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="text-center">
|
||||
<div class="text-3xl font-bold text-gray-600">{{ panoramaQueries.length }}/9</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">Queries Panorama</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<!-- Error Display -->
|
||||
<UAlert
|
||||
v-if="error"
|
||||
color="red"
|
||||
variant="soft"
|
||||
:title="error"
|
||||
:close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'red', variant: 'link' }"
|
||||
@close="error = null"
|
||||
/>
|
||||
|
||||
<!-- Tabs -->
|
||||
<UTabs v-model="selectedTab" :items="tabs">
|
||||
<template #default="{ item }">
|
||||
<!-- Table View -->
|
||||
<div v-if="item.key === 'table'" class="py-4">
|
||||
<MetabaseCardsTable
|
||||
:cards="cards"
|
||||
:loading="loading"
|
||||
@select="selectCard"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Cards View -->
|
||||
<div v-if="item.key === 'cards'" class="py-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<MetabaseCardDisplay
|
||||
v-for="card in cards"
|
||||
:key="card.id"
|
||||
:card="card"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panorama Queries -->
|
||||
<div v-if="item.key === 'panorama'" class="py-4 space-y-4">
|
||||
<UAlert
|
||||
color="blue"
|
||||
variant="soft"
|
||||
title="Queries del Panorama Facturador"
|
||||
description="Estas son las 9 queries documentadas en METABASE_QUERIES_PANORAMA.md"
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<MetabaseCardDisplay
|
||||
v-for="card in panoramaQueries"
|
||||
:key="card.id"
|
||||
:card="card"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
v-if="panoramaQueries.length < 9"
|
||||
color="yellow"
|
||||
variant="soft"
|
||||
:title="`Faltan ${9 - panoramaQueries.length} queries por encontrar`"
|
||||
description="Busca en la tabla las queries que contengan 'panorama' en su nombre"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Selected Card Detail -->
|
||||
<div v-if="item.key === 'detail' && selectedCard" class="py-4">
|
||||
<MetabaseCardDisplay :card="selectedCard" />
|
||||
</div>
|
||||
</template>
|
||||
</UTabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const cards = ref<any[]>([])
|
||||
const selectedCard = ref<any>(null)
|
||||
const selectedTab = ref(0)
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: 'table',
|
||||
label: 'Vista Tabla',
|
||||
icon: 'i-heroicons-table-cells'
|
||||
},
|
||||
{
|
||||
key: 'cards',
|
||||
label: 'Vista Cards',
|
||||
icon: 'i-heroicons-squares-2x2'
|
||||
},
|
||||
{
|
||||
key: 'panorama',
|
||||
label: 'Queries Panorama',
|
||||
icon: 'i-heroicons-chart-bar'
|
||||
},
|
||||
{
|
||||
key: 'detail',
|
||||
label: 'Detalle',
|
||||
icon: 'i-heroicons-document-magnifying-glass',
|
||||
disabled: !selectedCard.value
|
||||
}
|
||||
]
|
||||
|
||||
const nativeQueries = computed(() => {
|
||||
return cards.value.filter(card => {
|
||||
const type = card.query_type || card.dataset_query?.type
|
||||
return type === 'native'
|
||||
}).length
|
||||
})
|
||||
|
||||
const queryBuilderQueries = computed(() => {
|
||||
return cards.value.filter(card => {
|
||||
const type = card.query_type || card.dataset_query?.type
|
||||
return type === 'query'
|
||||
}).length
|
||||
})
|
||||
|
||||
const panoramaQueries = computed(() => {
|
||||
return cards.value.filter(card =>
|
||||
card.name?.toLowerCase().includes('panorama')
|
||||
)
|
||||
})
|
||||
|
||||
async function fetchCards() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const result = await $fetch('/api/metabase/cards?f=all')
|
||||
cards.value = Array.isArray(result) ? result : []
|
||||
} catch (e: any) {
|
||||
error.value = e.message || 'Error al cargar las cards de Metabase'
|
||||
console.error('Error fetching cards:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function refreshCards() {
|
||||
fetchCards()
|
||||
}
|
||||
|
||||
function selectCard(card: any) {
|
||||
selectedCard.value = card
|
||||
selectedTab.value = 3 // Switch to detail tab
|
||||
}
|
||||
|
||||
// Load cards on mount
|
||||
onMounted(() => {
|
||||
fetchCards()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user