@innertia-solutions/innertia-nuxt 0.1.53 → 0.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const api = useApi()
|
|
3
|
+
|
|
4
|
+
const { data, pending, error } = await useAsyncData(
|
|
5
|
+
'observability-database',
|
|
6
|
+
() => api.get('platform/observability/database'),
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
function fmtBytes(bytes) {
|
|
10
|
+
if (bytes == null) return '—'
|
|
11
|
+
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
12
|
+
let n = Number(bytes), i = 0
|
|
13
|
+
while (n >= 1024 && i < units.length - 1) { n /= 1024; i++ }
|
|
14
|
+
return `${n.toFixed(1)} ${units[i]}`
|
|
15
|
+
}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div>
|
|
20
|
+
<div v-if="pending" class="text-sm text-muted-foreground">Cargando…</div>
|
|
21
|
+
<div v-else-if="error" class="text-sm text-red-500">No se pudo cargar la capacidad de la BD.</div>
|
|
22
|
+
<div v-else class="space-y-6">
|
|
23
|
+
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
|
24
|
+
<div class="rounded-card border border-border p-4">
|
|
25
|
+
<p class="text-xs text-muted-foreground">Tamaño de la BD</p>
|
|
26
|
+
<p class="mt-1 text-lg font-bold text-foreground">{{ fmtBytes(data.capacity?.database_bytes) }}</p>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="rounded-card border border-border p-4">
|
|
29
|
+
<p class="text-xs text-muted-foreground">Conexiones</p>
|
|
30
|
+
<p class="mt-1 text-lg font-bold text-foreground">
|
|
31
|
+
{{ data.capacity?.connections?.used ?? '—' }} / {{ data.capacity?.connections?.max ?? '—' }}
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="rounded-card border border-border p-4">
|
|
35
|
+
<p class="text-xs text-muted-foreground">Disco usado</p>
|
|
36
|
+
<p class="mt-1 text-lg font-bold text-foreground">{{ data.disk?.percent != null ? data.disk.percent + '%' : '—' }}</p>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div>
|
|
41
|
+
<h2 class="mb-2 text-sm font-semibold text-foreground">Tablas más grandes</h2>
|
|
42
|
+
<table class="w-full text-sm">
|
|
43
|
+
<thead>
|
|
44
|
+
<tr class="text-left text-muted-foreground">
|
|
45
|
+
<th class="py-1">Tabla</th><th class="py-1">Tamaño</th><th class="py-1">Filas</th>
|
|
46
|
+
</tr>
|
|
47
|
+
</thead>
|
|
48
|
+
<tbody>
|
|
49
|
+
<tr v-for="t in data.capacity?.tables ?? []" :key="t.name" class="border-t border-border">
|
|
50
|
+
<td class="py-1">{{ t.name }}</td>
|
|
51
|
+
<td class="py-1">{{ fmtBytes(t.bytes) }}</td>
|
|
52
|
+
<td class="py-1">{{ Number(t.rows).toLocaleString('es-CL') }}</td>
|
|
53
|
+
</tr>
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const api = useApi()
|
|
3
|
+
const toast = useToast()
|
|
4
|
+
const reloadKey = ref(0)
|
|
5
|
+
|
|
6
|
+
const columns = [
|
|
7
|
+
{ key: 'last_seen', label: 'Última vez', sortable: true },
|
|
8
|
+
{ key: 'title', label: 'Error' },
|
|
9
|
+
{ key: 'exception_class', label: 'Clase' },
|
|
10
|
+
{ key: 'times_seen', label: 'Veces' },
|
|
11
|
+
{ key: 'status', label: 'Estado' },
|
|
12
|
+
{ key: 'actions', label: '' },
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const statusLabels = { open: 'Abierto', resolved: 'Resuelto', ignored: 'Ignorado' }
|
|
16
|
+
|
|
17
|
+
async function setStatus(row, status) {
|
|
18
|
+
try {
|
|
19
|
+
await api.post(`platform/observability/errors/${row.id}/status`, { status })
|
|
20
|
+
toast.success('Estado actualizado.')
|
|
21
|
+
reloadKey.value++
|
|
22
|
+
} catch (e) {
|
|
23
|
+
toast.error(e?.data?.message ?? 'No se pudo cambiar el estado.')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<Table.Standard
|
|
30
|
+
:key="reloadKey"
|
|
31
|
+
name="observability-errors"
|
|
32
|
+
endpoint="platform/observability/errors"
|
|
33
|
+
:columns="columns"
|
|
34
|
+
search-placeholder="Buscar error…"
|
|
35
|
+
>
|
|
36
|
+
<template #status="{ value }">{{ statusLabels[value] ?? value }}</template>
|
|
37
|
+
<template #actions="{ row }">
|
|
38
|
+
<div class="flex gap-2">
|
|
39
|
+
<button class="text-xs text-muted-foreground hover:text-foreground" @click="setStatus(row, 'resolved')">Resolver</button>
|
|
40
|
+
<button class="text-xs text-muted-foreground hover:text-foreground" @click="setStatus(row, 'ignored')">Ignorar</button>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
</Table.Standard>
|
|
44
|
+
</template>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const props = defineProps({
|
|
3
|
+
channel: { type: String, default: 'operational' },
|
|
4
|
+
})
|
|
5
|
+
|
|
6
|
+
const endpoint = computed(() => `platform/observability/logs?channel=${props.channel}`)
|
|
7
|
+
|
|
8
|
+
const columns = [
|
|
9
|
+
{ key: 'occurred_at', label: 'Fecha', sortable: true },
|
|
10
|
+
{ key: 'level', label: 'Nivel' },
|
|
11
|
+
{ key: 'message', label: 'Mensaje' },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const levelOf = (row) => row?.payload?.level ?? '—'
|
|
15
|
+
const messageOf = (row) => row?.payload?.message ?? '—'
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<Table.Standard
|
|
20
|
+
:name="`observability-logs-${channel}`"
|
|
21
|
+
:endpoint="endpoint"
|
|
22
|
+
:columns="columns"
|
|
23
|
+
search-placeholder="Buscar en logs…"
|
|
24
|
+
>
|
|
25
|
+
<template #level="{ row }">{{ levelOf(row) }}</template>
|
|
26
|
+
<template #message="{ row }">{{ messageOf(row) }}</template>
|
|
27
|
+
</Table.Standard>
|
|
28
|
+
</template>
|