Files
analiticaNucleo/nuxt4-app/app/components/metabase/MetabaseCardDisplay.vue
josedario87 90aebbde3d
All checks were successful
build-and-deploy / build (push) Successful in 41s
build-and-deploy / deploy (push) Successful in 3s
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
2025-10-14 01:34:56 -06:00

188 lines
5.0 KiB
Vue

<template>
<UCard>
<template #header>
<div class="flex justify-between items-start">
<div class="flex-1">
<h3 class="text-lg font-semibold">{{ card.name }}</h3>
<p v-if="card.description" class="text-sm text-gray-500 dark:text-gray-400 mt-1">
{{ card.description }}
</p>
</div>
<UBadge :color="getStatusColor(card)">
ID: {{ card.id }}
</UBadge>
</div>
</template>
<!-- Card Details -->
<div class="space-y-3">
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="font-medium">Database ID:</span>
<span class="ml-2">{{ card.database_id }}</span>
</div>
<div>
<span class="font-medium">Query Type:</span>
<span class="ml-2">{{ card.query_type || card.dataset_query?.type }}</span>
</div>
<div>
<span class="font-medium">Collection:</span>
<span class="ml-2">{{ card.collection_id || 'Root' }}</span>
</div>
<div>
<span class="font-medium">Created:</span>
<span class="ml-2">{{ formatDate(card.created_at) }}</span>
</div>
</div>
<!-- Query Preview -->
<div v-if="card.dataset_query?.native?.query" class="mt-4">
<details class="group">
<summary class="cursor-pointer text-sm font-medium text-primary">
Ver SQL
</summary>
<pre class="mt-2 p-3 bg-gray-50 dark:bg-gray-900 rounded text-xs overflow-x-auto">{{ card.dataset_query.native.query }}</pre>
</details>
</div>
<!-- Actions -->
<div class="flex gap-2 mt-4 pt-4 border-t dark:border-gray-700">
<UButton
@click="executeQuery"
:loading="executing"
color="primary"
size="sm"
>
Ejecutar Query
</UButton>
<UButton
@click="executeQueryCached"
:loading="executingCached"
color="gray"
variant="soft"
size="sm"
>
Con Caché
</UButton>
<UDropdown :items="exportItems">
<UButton
color="gray"
variant="soft"
size="sm"
trailing-icon="i-heroicons-chevron-down-20-solid"
>
Exportar
</UButton>
</UDropdown>
</div>
<!-- Query Results -->
<div v-if="queryResult" class="mt-4">
<div class="flex justify-between items-center mb-2">
<h4 class="font-medium text-sm">Resultados</h4>
<UBadge color="green">
{{ queryResult.data?.rows?.length || 0 }} filas en {{ queryResult.running_time || 0 }}ms
</UBadge>
</div>
<div class="overflow-x-auto">
<pre class="p-3 bg-gray-50 dark:bg-gray-900 rounded text-xs">{{ JSON.stringify(queryResult.data, null, 2) }}</pre>
</div>
</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"
/>
</div>
</UCard>
</template>
<script setup lang="ts">
const props = defineProps<{
card: any
}>()
const executing = ref(false)
const executingCached = ref(false)
const queryResult = ref<any>(null)
const error = ref<string | null>(null)
const exportItems = [[
{
label: 'CSV',
icon: 'i-heroicons-document-text',
click: () => downloadExport('csv')
},
{
label: 'JSON',
icon: 'i-heroicons-code-bracket',
click: () => downloadExport('json')
},
{
label: 'XLSX',
icon: 'i-heroicons-table-cells',
click: () => downloadExport('xlsx')
}
]]
function getStatusColor(card: any) {
if (card.archived) return 'red'
if (card.dataset) return 'blue'
return 'gray'
}
function formatDate(dateString: string) {
if (!dateString) return 'N/A'
return new Date(dateString).toLocaleDateString('es-ES', {
year: 'numeric',
month: 'short',
day: 'numeric'
})
}
async function executeQuery() {
executing.value = true
error.value = null
queryResult.value = null
try {
const result = await $fetch(`/api/metabase/cards/${props.card.id}/query`, {
method: 'POST'
})
queryResult.value = result
} catch (e: any) {
error.value = e.message || 'Error al ejecutar la query'
} finally {
executing.value = false
}
}
async function executeQueryCached() {
executingCached.value = true
error.value = null
queryResult.value = null
try {
const result = await $fetch(`/api/metabase/cards/${props.card.id}/query`)
queryResult.value = result
} catch (e: any) {
error.value = e.message || 'Error al ejecutar la query con caché'
} finally {
executingCached.value = false
}
}
function downloadExport(format: 'csv' | 'json' | 'xlsx') {
const url = `${window.location.origin}/api/metabase/cards/${props.card.id}/query/${format}`
window.open(url, '_blank')
}
</script>