Add services status view with polling and control actions
CI / basic-check (push) Has been cancelled

This commit is contained in:
Perplexity Bot
2026-08-03 22:22:59 +00:00
parent be55c35c3a
commit 186aae1b5f
10 changed files with 204 additions and 25 deletions
+3 -2
View File
@@ -1,8 +1,9 @@
import { AppLayout } from '../components/AppLayout';
import type { ViewKey } from '../types';
export function DiscoveryJobsView() {
export function DiscoveryJobsView({ onNavigate }: { onNavigate: (v: ViewKey) => void }) {
return (
<AppLayout current="discovery" onNavigate={() => {}}>
<AppLayout current="discovery" onNavigate={onNavigate}>
<header className="header">
<h1 className="page-title">Discovery Jobs</h1>
</header>
+3 -2
View File
@@ -1,8 +1,9 @@
import { AppLayout } from '../components/AppLayout';
import type { ViewKey } from '../types';
export function IPsView() {
export function IPsView({ onNavigate }: { onNavigate: (v: ViewKey) => void }) {
return (
<AppLayout current="ips" onNavigate={() => {}}>
<AppLayout current="ips" onNavigate={onNavigate}>
<header className="header">
<h1 className="page-title">IPs</h1>
</header>
+3 -2
View File
@@ -1,8 +1,9 @@
import { useState, useEffect } from 'react';
import { AppLayout } from '../components/AppLayout';
import { fetchObjects } from '../lib/api';
import type { ViewKey } from '../types';
export function InventoryView() {
export function InventoryView({ onNavigate }: { onNavigate: (v: ViewKey) => void }) {
const [objects, setObjects] = useState<Awaited<ReturnType<typeof fetchObjects>>>([]);
useEffect(() => {
@@ -10,7 +11,7 @@ export function InventoryView() {
}, []);
return (
<AppLayout current="inventory" onNavigate={() => {}}>
<AppLayout current="inventory" onNavigate={onNavigate}>
<header className="header">
<h1 className="page-title">Inventory</h1>
</header>
+108
View File
@@ -0,0 +1,108 @@
import { useCallback, useEffect, useState } from 'react';
import { AppLayout } from '../components/AppLayout';
import { fetchServices, serviceAction } from '../lib/api';
import type { ServiceInfo, ViewKey } from '../types';
function stateClass(state: string) {
switch (state) {
case 'active':
return 'status status-active';
case 'failed':
return 'status status-failed';
case 'activating':
case 'deactivating':
return 'status status-pending';
default:
return 'status status-inactive';
}
}
export function ServicesView({ onNavigate }: { onNavigate: (v: ViewKey) => void }) {
const [services, setServices] = useState<ServiceInfo[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState<string | null>(null);
const load = useCallback(async () => {
try {
setError(null);
const data = await fetchServices();
setServices(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Errore sconosciuto');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
load();
const timer = setInterval(load, 5000);
return () => clearInterval(timer);
}, [load]);
const runAction = async (name: string, action: 'start' | 'stop' | 'restart') => {
try {
setBusy(`${name}:${action}`);
await serviceAction(name, action);
await load();
} catch (err) {
setError(err instanceof Error ? err.message : 'Errore sconosciuto');
} finally {
setBusy(null);
}
};
return (
<AppLayout current="services" onNavigate={onNavigate}>
<header className="header">
<h1 className="page-title">Services</h1>
<button className="btn" onClick={load}>Refresh</button>
</header>
{error && <div className="alert">{error}</div>}
<div className="card">
{loading ? (
<p className="empty">Caricamento stato servizi...</p>
) : (
<table className="table">
<thead>
<tr>
<th>Service</th>
<th>State</th>
<th>Substate</th>
<th>Loaded</th>
<th>PID</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{services.length === 0 && (
<tr>
<td colSpan={6} className="empty">Nessun servizio disponibile</td>
</tr>
)}
{services.map(service => (
<tr key={service.name}>
<td>{service.name}</td>
<td><span className={stateClass(service.active_state)}>{service.active_state}</span></td>
<td>{service.sub_state}</td>
<td>{service.loaded}</td>
<td>{service.pid ?? '-'}</td>
<td>
<div className="actions">
<button className="btn" disabled={!!busy} onClick={() => runAction(service.name, 'start')}>Start</button>
<button className="btn" disabled={!!busy} onClick={() => runAction(service.name, 'stop')}>Stop</button>
<button className="btn btn-primary" disabled={!!busy} onClick={() => runAction(service.name, 'restart')}>Restart</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</AppLayout>
);
}
+5 -5
View File
@@ -1,17 +1,17 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { AppLayout } from '../components/AppLayout';
import type { ViewKey } from '../types';
import { fetchObjects } from '../lib/api';
export function TopologyView() {
export function TopologyView({ onNavigate }: { onNavigate: (v: ViewKey) => void }) {
const [objects, setObjects] = useState<Awaited<ReturnType<typeof fetchObjects>>>([]);
useState(() => {
useEffect(() => {
fetchObjects().then(setObjects);
});
}, []);
return (
<AppLayout current="topology" onNavigate={() => {}}>
<AppLayout current="topology" onNavigate={onNavigate}>
<header className="header">
<h1 className="page-title">Topology</h1>
</header>