feat: connect alpine operations and wizard to admin api
This commit is contained in:
+22
-16
@@ -13,6 +13,7 @@
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
</head>
|
||||
<body x-data="catalog()">
|
||||
|
||||
@@ -118,23 +119,23 @@
|
||||
<div class="stats-row">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label" data-i18n="stats.total">Total operations</div>
|
||||
<div class="stat-value" x-text="operations.length">—</div>
|
||||
<div class="stat-delta up">+3 this month</div>
|
||||
<div class="stat-value" x-text="formatMetricNumber(stats.total)">—</div>
|
||||
<div class="stat-delta" x-text="stats.total ? 'Catalog synced with backend' : 'No operations yet'"></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label" data-i18n="stats.active">Active</div>
|
||||
<div class="stat-value" x-text="operations.filter(o => o.status === 'active').length">—</div>
|
||||
<div class="stat-delta" x-text="operations.length ? Math.round(operations.filter(o=>o.status==='active').length/operations.length*100)+'% of total' : ''">—</div>
|
||||
<div class="stat-value" x-text="formatMetricNumber(stats.active)">—</div>
|
||||
<div class="stat-delta" x-text="stats.total ? Math.round((stats.active / stats.total) * 100) + '% of total' : 'No published tools yet'">—</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label" data-i18n="stats.requests">Requests today</div>
|
||||
<div class="stat-value">4,821</div>
|
||||
<div class="stat-delta up">+12% vs yesterday</div>
|
||||
<div class="stat-value" x-text="formatMetricNumber(stats.requestsToday)">—</div>
|
||||
<div class="stat-delta" x-text="stats.requestsToday ? 'Summed from operation usage' : 'No traffic recorded today'"></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label" data-i18n="stats.latency">Avg. latency</div>
|
||||
<div class="stat-value">187ms</div>
|
||||
<div class="stat-delta down">+23ms this week</div>
|
||||
<div class="stat-value" x-text="stats.averageLatencyMs ? stats.averageLatencyMs + 'ms' : '—'">—</div>
|
||||
<div class="stat-delta" x-text="stats.averageLatencyMs ? 'Weighted by today\\'s calls' : 'Waiting for runtime samples'"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -233,15 +234,15 @@
|
||||
:class="{ 'has-value': filterAgent, open: openDropdown === 'agent' }"
|
||||
@click="toggleDropdown('agent')"
|
||||
>
|
||||
<span x-text="filterAgent ? activeAgents.find(a => a.id === filterAgent)?.display_name : 'Agent'"></span>
|
||||
<span x-text="filterAgent ? activeAgents.find(a => a.agent_id === filterAgent)?.display_name : 'Agent'"></span>
|
||||
<svg class="chevron" width="10" height="10"><use href="icons/general/chevron-down.svg#icon"/></svg>
|
||||
</button>
|
||||
<div class="dropdown-menu" x-show="openDropdown === 'agent'" x-transition>
|
||||
<template x-for="a in activeAgents" :key="a.id">
|
||||
<template x-for="a in activeAgents" :key="a.agent_id">
|
||||
<button
|
||||
class="dropdown-item"
|
||||
:class="{ selected: filterAgent === a.id }"
|
||||
@click="setAgent(a.id)"
|
||||
:class="{ selected: filterAgent === a.agent_id }"
|
||||
@click="setAgent(a.agent_id)"
|
||||
>
|
||||
<span x-text="a.display_name"></span>
|
||||
<svg class="check" width="12" height="12"><use href="icons/general/check.svg#icon"/></svg>
|
||||
@@ -321,8 +322,13 @@
|
||||
<p data-i18n="ops.empty.sub">Try adjusting your search or filters.</p>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" x-show="!loading && loadError">
|
||||
<h3>Backend connection failed</h3>
|
||||
<p x-text="loadError"></p>
|
||||
</div>
|
||||
|
||||
<!-- Table itself -->
|
||||
<table x-show="!loading && totalFiltered > 0">
|
||||
<table x-show="!loading && totalFiltered > 0 && !loadError">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="th-sortable" :class="{ 'sort-active': sort.startsWith('name') }" @click="setSort(sort === 'name_asc' ? 'name_desc' : 'name_asc')">
|
||||
@@ -350,7 +356,7 @@
|
||||
<div class="op-name" x-text="op.name"></div>
|
||||
<div class="op-display" x-text="op.display_name"></div>
|
||||
<div class="op-agent-badges" x-show="(agentsByOpId[op.id] || []).length > 0">
|
||||
<template x-for="a in (agentsByOpId[op.id] || [])" :key="a.id">
|
||||
<template x-for="a in (agentsByOpId[op.id] || [])" :key="a.agent_id">
|
||||
<span class="op-agent-badge" x-text="a.display_name"></span>
|
||||
</template>
|
||||
</div>
|
||||
@@ -368,7 +374,7 @@
|
||||
<td class="col-url">
|
||||
<div class="target-url-cell">
|
||||
<span x-text="truncateUrl(op.target_url)" :title="op.target_url"></span>
|
||||
<a :href="op.target_url && op.target_url.startsWith('http') ? op.target_url : '#'" target="_blank" rel="noopener">
|
||||
<a :href="op.target_url && op.target_url.startsWith('http') ? op.target_url : '#'" target="_blank" rel="noopener" :style="!op.target_url ? 'opacity:.35;pointer-events:none;' : ''">
|
||||
<svg width="11" height="11"><use href="icons/general/external.svg#icon"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
@@ -389,7 +395,7 @@
|
||||
</table>
|
||||
|
||||
<!-- Footer / pagination -->
|
||||
<div class="table-footer" x-show="!loading && totalFiltered > 0">
|
||||
<div class="table-footer" x-show="!loading && totalFiltered > 0 && !loadError">
|
||||
<span class="table-footer-info">
|
||||
Showing <span x-text="pageStart"></span>–<span x-text="pageEnd"></span> of <span x-text="totalFiltered"></span> operations
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user