This commit is contained in:
Lizandro Guarnizo
2026-05-14 22:30:31 -05:00
parent 277ad6bb63
commit 61b3eca31d
16 changed files with 1341 additions and 25 deletions
+41 -3
View File
@@ -29,6 +29,7 @@
<th class="py-2 px-4 border-b">Servicio vinculado</th>
<th class="py-2 px-4 border-b">Orden</th>
<th class="py-2 px-4 border-b w-10">Estado</th>
<th class="py-2 px-4 border-b w-20 text-center">Health</th>
<th class="py-2 px-4 border-b w-24"></th>
</tr>
</thead>
@@ -47,6 +48,23 @@
:class="item.activo ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'"
class="text-xs px-2 py-0.5 rounded-full"></span>
</td>
<!-- Health check -->
<td class="py-2 px-4 text-center">
<template x-if="!item.health_url">
<span class="inline-block w-3 h-3 rounded-full bg-gray-300" title="Sin URL de health"></span>
</template>
<template x-if="item.health_url">
<div class="flex flex-col items-center gap-1">
<span :class="healthStatus[item.ID] === 'ok' ? 'bg-green-400' : healthStatus[item.ID] === 'fail' ? 'bg-red-400' : 'bg-yellow-300'"
class="inline-block w-3 h-3 rounded-full"
:title="healthInfo[item.ID] || 'Click para verificar'"></span>
<button @click="checkHealth(item)" class="text-xs text-gray-400 hover:text-[#8eb02f]" title="Verificar">
<span x-show="healthLoading[item.ID]"></span>
<span x-show="!healthLoading[item.ID]">check</span>
</button>
</div>
</template>
</td>
<td class="py-2 px-4 flex gap-2 justify-end">
<a :href="`/docs/${item.slug}`" target="_blank" title="Ver documentación" class="text-green-500 hover:text-green-700">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
@@ -111,6 +129,12 @@
<input type="text" x-model="form.logo_url"
class="border border-gray-300 rounded w-full p-2 text-sm">
</div>
<div class="col-span-2">
<label class="block text-xs font-medium mb-1">Health Check URL <span class="text-gray-400 font-normal">(opcional)</span></label>
<input type="url" x-model="form.health_url" placeholder="https://api.tuservicio.com/health"
class="border border-gray-300 rounded w-full p-2 text-sm font-mono">
<p class="text-xs text-gray-400 mt-0.5">Se usará para verificar que el servicio está activo. Debe devolver HTTP 2xx.</p>
</div>
<div class="col-span-2">
<label class="block text-xs font-medium mb-1">Servicio vinculado <span class="text-gray-400 font-normal">(opcional)</span></label>
<select x-model.number="form.servicio_id" class="border border-gray-300 rounded w-full p-2 text-sm">
@@ -160,7 +184,8 @@ function saasApp() {
loading: false,
showModal: false, showDeleteModal: false,
editId: null, deleteId: null,
form: { nombre: '', slug: '', descripcion: '', logo_url: '', servicio_id: null, activo: true, orden: 0 },
form: { nombre: '', slug: '', descripcion: '', logo_url: '', health_url: '', servicio_id: null, activo: true, orden: 0 },
healthStatus: {}, healthInfo: {}, healthLoading: {},
init() { this.load(1); },
@@ -190,7 +215,7 @@ function saasApp() {
openAdd() {
this.editId = null;
this.form = { nombre: '', slug: '', descripcion: '', logo_url: '', servicio_id: null, activo: true, orden: 0 };
this.form = { nombre: '', slug: '', descripcion: '', logo_url: '', health_url: '', servicio_id: null, activo: true, orden: 0 };
this.showModal = true;
},
@@ -198,12 +223,25 @@ function saasApp() {
this.editId = item.ID;
this.form = {
nombre: item.nombre, slug: item.slug, descripcion: item.descripcion,
logo_url: item.logo_url, servicio_id: item.servicio_id,
logo_url: item.logo_url, health_url: item.health_url || '',
servicio_id: item.servicio_id,
activo: item.activo, orden: item.orden
};
this.showModal = true;
},
async checkHealth(item) {
this.$set(this.healthLoading, item.ID, true);
const res = await fetch(`/app/saas/${item.ID}/health`);
const d = await res.json();
this.$set(this.healthStatus, item.ID, d.ok ? 'ok' : 'fail');
const info = d.ok
? `HTTP ${d.http_status} · ${d.latency_ms}ms`
: (d.error || `HTTP ${d.http_status}`);
this.$set(this.healthInfo, item.ID, info);
this.$set(this.healthLoading, item.ID, false);
},
submitForm() {
const url = this.editId ? `/app/saas/${this.editId}` : '/app/saas';
const method = this.editId ? 'PUT' : 'POST';