feat: mejorar visualización de resultados de queries en Metabase Debug
All checks were successful
build-and-deploy / build (push) Successful in 43s
build-and-deploy / deploy (push) Successful in 3s

- Agregar tabla estructurada para mostrar resultados de queries
- Agregar botón de copiar JSON profesional con feedback visual
- Agregar formato automático de números y fechas según tipo de columna
- Mantener vista JSON colapsable para ver datos completos
- Mejorar UX con tabla responsive y estilos consistentes
This commit is contained in:
2025-10-14 03:42:52 -06:00
parent 02d17b816b
commit c69c3bdafc

View File

@@ -82,9 +82,21 @@
<div v-if="queryResult" class="mt-4"> <div v-if="queryResult" class="mt-4">
<div class="flex flex-wrap justify-between items-center gap-2 mb-2"> <div class="flex flex-wrap justify-between items-center gap-2 mb-2">
<h4 class="font-medium text-sm">Resultados</h4> <h4 class="font-medium text-sm">Resultados</h4>
<UBadge color="green"> <div class="flex items-center gap-2">
{{ queryResult.data?.rows?.length || 0 }} filas en {{ queryResult.running_time || 0 }}ms <UBadge color="green">
</UBadge> {{ queryResult.data?.rows?.length || 0 }} filas en {{ queryResult.running_time || 0 }}ms
</UBadge>
<UButton
v-if="queryResult.data"
@click="copyResults"
color="gray"
variant="soft"
size="xs"
icon="i-heroicons-clipboard-document"
>
{{ copied ? 'Copiado!' : 'Copiar JSON' }}
</UButton>
</div>
</div> </div>
<!-- Empty State --> <!-- Empty State -->
@@ -99,8 +111,43 @@
</div> </div>
<!-- Data Display --> <!-- Data Display -->
<div v-else class="overflow-x-auto"> <div v-else>
<pre class="p-3 bg-gray-50 dark:bg-gray-900 rounded text-xs whitespace-pre-wrap break-words">{{ JSON.stringify(queryResult.data, null, 2) }}</pre> <!-- Table View -->
<div v-if="queryResult.data.cols && queryResult.data.rows" class="overflow-x-auto mb-4">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th
v-for="col in queryResult.data.cols"
:key="col.name"
class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
>
{{ col.display_name || col.name }}
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
<tr v-for="(row, rowIndex) in queryResult.data.rows" :key="rowIndex">
<td
v-for="(cell, cellIndex) in row"
:key="cellIndex"
class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100"
>
{{ formatCell(cell, queryResult.data.cols[cellIndex]) }}
</td>
</tr>
</tbody>
</table>
</div>
<!-- JSON View (collapsible) -->
<details class="group">
<summary class="cursor-pointer text-sm font-medium text-primary flex items-center gap-2">
<span>Ver JSON completo</span>
<UIcon name="i-heroicons-chevron-down-20-solid" class="w-4 h-4 transition-transform group-open:rotate-180" />
</summary>
<pre class="mt-2 p-3 bg-gray-50 dark:bg-gray-900 rounded text-xs whitespace-pre-wrap break-words">{{ JSON.stringify(queryResult.data, null, 2) }}</pre>
</details>
</div> </div>
</div> </div>
@@ -126,6 +173,7 @@ const executing = ref(false)
const executingCached = ref(false) const executingCached = ref(false)
const queryResult = ref<any>(null) const queryResult = ref<any>(null)
const error = ref<string | null>(null) const error = ref<string | null>(null)
const copied = ref(false)
const exportItems = [[ const exportItems = [[
{ {
@@ -203,4 +251,39 @@ function downloadExport(format: 'csv' | 'json' | 'xlsx') {
const url = `${window.location.origin}/api/metabase/cards/${props.card.id}/query/${format}` const url = `${window.location.origin}/api/metabase/cards/${props.card.id}/query/${format}`
window.open(url, '_blank') window.open(url, '_blank')
} }
async function copyResults() {
try {
await navigator.clipboard.writeText(JSON.stringify(queryResult.value.data, null, 2))
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch (e) {
console.error('Error al copiar:', e)
}
}
function formatCell(value: any, col: any) {
if (value === null || value === undefined) return '-'
// Format numbers based on type
if (col.base_type === 'type/Float' || col.base_type === 'type/Decimal') {
return new Intl.NumberFormat('es-ES', {
minimumFractionDigits: 0,
maximumFractionDigits: 2
}).format(value)
}
if (col.base_type === 'type/Integer') {
return new Intl.NumberFormat('es-ES').format(value)
}
// Format dates
if (col.base_type === 'type/Date' || col.base_type === 'type/DateTime') {
return new Date(value).toLocaleDateString('es-ES')
}
return value
}
</script> </script>