Add a host filter to the Docker page
Dockhand aggregates containers across every Docker host it manages into one list, with no way to narrow it down to a single host. Add a host dropdown next to Refresh (only shown when more than one host is present) that filters the table client-side over the already-fetched container list. Resets whenever the selected Dockhand integration changes, so a stale host name from a previous integration can't linger. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
const [data, setData] = useState<DockhandContainersResponse | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [actingOnContainer, setActingOnContainer] = useState<string | null>(null);
|
||||
const [hostFilter, setHostFilter] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
api.integrations
|
||||
@@ -60,6 +61,7 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setHostFilter("");
|
||||
if (selected?.enabled) {
|
||||
loadContainers(selected.id);
|
||||
} else {
|
||||
@@ -68,6 +70,9 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedId]);
|
||||
|
||||
const hosts = [...new Set((data?.containers ?? []).map((c) => c.environmentName))].sort();
|
||||
const filteredContainers = (data?.containers ?? []).filter((c) => !hostFilter || c.environmentName === hostFilter);
|
||||
|
||||
async function containerAction(c: DockhandContainer, action: "start" | "stop" | "restart") {
|
||||
if (!selectedId) return;
|
||||
if (action === "stop" && !confirm(`Stop container "${c.name}"?`)) return;
|
||||
@@ -132,7 +137,22 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
</span>
|
||||
)}
|
||||
</h3>
|
||||
<div className="card-actions">
|
||||
<div className="card-actions d-flex gap-2">
|
||||
{hosts.length > 1 && (
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
style={{ width: "auto" }}
|
||||
value={hostFilter}
|
||||
onChange={(e) => setHostFilter(e.target.value)}
|
||||
>
|
||||
<option value="">All hosts</option>
|
||||
{hosts.map((host) => (
|
||||
<option key={host} value={host}>
|
||||
{host}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
onClick={() => selected && loadContainers(selected.id)}
|
||||
@@ -154,7 +174,7 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.containers.map((c) => (
|
||||
{filteredContainers.map((c) => (
|
||||
<tr key={`${c.environmentId}:${c.id}`}>
|
||||
<td>{c.name}</td>
|
||||
<td className="text-secondary">{c.environmentName}</td>
|
||||
@@ -197,10 +217,10 @@ export default function Docker({ user }: { user: CurrentUser }) {
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
{data?.containers.length === 0 && (
|
||||
{data && filteredContainers.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={canEdit ? 5 : 4} className="text-secondary text-center">
|
||||
No containers found across any environment.
|
||||
{hostFilter ? `No containers found on "${hostFilter}".` : "No containers found across any environment."}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user