feat: mejorar visualización de resultados de queries en Metabase Debug
- 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:
@@ -82,9 +82,21 @@
|
||||
<div v-if="queryResult" class="mt-4">
|
||||
<div class="flex flex-wrap justify-between items-center gap-2 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 class="flex items-center gap-2">
|
||||
<UBadge color="green">
|
||||
{{ 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>
|
||||
|
||||
<!-- Empty State -->
|
||||
@@ -99,8 +111,43 @@
|
||||
</div>
|
||||
|
||||
<!-- Data Display -->
|
||||
<div v-else class="overflow-x-auto">
|
||||
<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>
|
||||
<div v-else>
|
||||
<!-- 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>
|
||||
|
||||
@@ -126,6 +173,7 @@ const executing = ref(false)
|
||||
const executingCached = ref(false)
|
||||
const queryResult = ref<any>(null)
|
||||
const error = ref<string | null>(null)
|
||||
const copied = ref(false)
|
||||
|
||||
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}`
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user