Files
docker-service-discovery/frontend/src/App.js
2026-03-28 13:41:29 +01:00

104 lines
3.1 KiB
JavaScript

import React, { useState, useEffect, useMemo } from 'react';
import './App.css';
import { getServices } from './api';
import ServiceCard from './components/ServiceCard';
import SearchBox from './components/SearchBox'; // Import SearchBox
import FilterBox from './components/FilterBox'; // Import FilterBox
function App() {
const [services, setServices] = useState([]);
const [searchTerm, setSearchTerm] = useState(''); // Add state for search term
const [selectedLabels, setSelectedLabels] = useState([]);
async function fetchServices() {
const fetchedServices = await getServices();
setServices(fetchedServices);
}
useEffect(() => {
fetchServices();
const ws = new WebSocket('ws://localhost:3001/api/events');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'container' && (message.action === 'start' || message.action === 'stop')) {
fetchServices();
}
};
ws.onclose = () => {
// Optional: handle reconnection logic here
};
return () => {
ws.close();
};
}, []);
// Extract unique labels
const allLabels = useMemo(() => {
const labels = new Set();
services.forEach(service => {
for (const key in service.labels) {
labels.add(`${key}:${service.labels[key]}`);
}
});
return Array.from(labels).sort();
}, [services]);
// Handler for toggling labels
const handleLabelToggle = (labelToToggle) => {
setSelectedLabels(prevSelectedLabels => {
if (prevSelectedLabels.includes(labelToToggle)) {
return prevSelectedLabels.filter(label => label !== labelToToggle);
} else {
return [...prevSelectedLabels, labelToToggle];
}
});
};
// Filtering logic
const filteredServices = services.filter(service => {
const matchesSearchTerm = service.name.toLowerCase().includes(searchTerm.toLowerCase());
let matchesFilterTerm = true;
// Update filtering logic to use selectedLabels
if (selectedLabels.length > 0) {
matchesFilterTerm = selectedLabels.every(selectedLabel => {
const [key, value] = selectedLabel.split(':');
if (value) { // key:value format
return service.labels[key] === value;
} else { // key only format (e.g., "has:env" was replaced by just "env")
return Object.keys(service.labels).includes(key);
}
});
}
return matchesSearchTerm && matchesFilterTerm;
});
return (
<div className="App">
<header className="App-header">
<h1>Docker Service Display</h1>
<div className="flex space-x-2 w-fit">
<SearchBox searchTerm={searchTerm} onSearchChange={setSearchTerm} />
<FilterBox
availableLabels={allLabels}
selectedLabels={selectedLabels}
onLabelToggle={handleLabelToggle}
/>
</div>
</header>
<main className="service-grid">
{filteredServices.map(service => (
<ServiceCard key={service.id} service={service} />
))}
</main>
</div>
);
}
export default App;