El contenido de los tabs no se mostraba porque faltaba la propiedad 'slot' en cada item del array tabs. Nuxt UI UTabs requiere esta propiedad para vincular los slots nombrados con sus respectivos tabs. Cambios: - Agregar propiedad 'slot' a cada item en el array tabs - Ahora los slots nombrados (#table, #cards, #panorama, #detail) se vinculan correctamente con sus tabs correspondientes
215 lines
5.8 KiB
Vue
215 lines
5.8 KiB
Vue
<template>
|
|
<div class="container mx-auto px-4 py-8">
|
|
<div class="space-y-6">
|
|
<!-- Header -->
|
|
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-4">
|
|
<div class="flex-1">
|
|
<h1 class="text-2xl sm:text-3xl font-bold">Metabase Debug</h1>
|
|
<p class="text-sm sm:text-base 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"
|
|
class="w-full sm:w-auto"
|
|
>
|
|
Actualizar
|
|
</UButton>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-4 gap-3 sm:gap-4">
|
|
<UCard>
|
|
<div class="text-center">
|
|
<div class="text-2xl sm:text-3xl font-bold text-primary">{{ cards.length }}</div>
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">Total Cards</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<div class="text-center">
|
|
<div class="text-2xl sm:text-3xl font-bold text-primary">{{ nativeQueries }}</div>
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">SQL Nativo</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<div class="text-center">
|
|
<div class="text-2xl sm:text-3xl font-bold text-green-600">{{ queryBuilderQueries }}</div>
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">Query Builder</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<div class="text-center">
|
|
<div class="text-2xl sm:text-3xl font-bold text-gray-600">{{ panoramaQueries.length }}/9</div>
|
|
<div class="text-xs sm:text-sm text-gray-600 dark:text-gray-400">Queries Panorama</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
|
|
<!-- Error Display -->
|
|
<UAlert
|
|
v-if="error"
|
|
color="error"
|
|
variant="soft"
|
|
:title="error"
|
|
:close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'error', variant: 'link' }"
|
|
@close="error = null"
|
|
/>
|
|
|
|
<!-- Tabs -->
|
|
<UTabs v-model="selectedTab" :items="tabs">
|
|
<!-- Table View -->
|
|
<template #table>
|
|
<div class="py-4">
|
|
<MetabaseCardsTable
|
|
:cards="cards"
|
|
:loading="loading"
|
|
@select="selectCard"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Cards View -->
|
|
<template #cards>
|
|
<div 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>
|
|
</template>
|
|
|
|
<!-- Panorama Queries -->
|
|
<template #panorama>
|
|
<div class="py-4 space-y-4">
|
|
<UAlert
|
|
color="info"
|
|
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="warning"
|
|
variant="soft"
|
|
:title="`Faltan ${9 - panoramaQueries.length} queries por encontrar`"
|
|
description="Busca en la tabla las queries que contengan 'panorama' en su nombre"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Selected Card Detail -->
|
|
<template #detail>
|
|
<div v-if="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',
|
|
slot: 'table'
|
|
},
|
|
{
|
|
key: 'cards',
|
|
label: 'Vista Cards',
|
|
icon: 'i-heroicons-squares-2x2',
|
|
slot: 'cards'
|
|
},
|
|
{
|
|
key: 'panorama',
|
|
label: 'Queries Panorama',
|
|
icon: 'i-heroicons-chart-bar',
|
|
slot: 'panorama'
|
|
},
|
|
{
|
|
key: 'detail',
|
|
label: 'Detalle',
|
|
icon: 'i-heroicons-document-magnifying-glass',
|
|
disabled: !selectedCard.value,
|
|
slot: 'detail'
|
|
}
|
|
]
|
|
|
|
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>
|