"use client"; import { useEffect, useState } from "react"; import { Search, Server, Activity } from "lucide-react"; import ContainerGroup from "@/components/ContainerGroup"; import SearchBar from "@/components/SearchBar"; import GrafanaWidget from "@/components/GrafanaWidget"; import UnifiWidget from "@/components/UnifiWidget"; import SynologyWidget from "@/components/SynologyWidget"; import { Container } from "@/types"; export default function Home() { const [containers, setContainers] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [loading, setLoading] = useState(true); useEffect(() => { fetchContainers(); const interval = setInterval(fetchContainers, 10000); // Refresh every 10s return () => clearInterval(interval); }, []); const fetchContainers = async () => { try { const response = await fetch("/api/containers"); const data = await response.json(); setContainers(data); setLoading(false); } catch (error) { console.error("Failed to fetch containers:", error); setLoading(false); } }; const groupContainers = (containers: Container[]) => { return { media: containers.filter((c) => [ "sonarr", "radarr", "lidarr", "whisparr", "prowlarr", "bazarr", "tautulli", "overseerr", "ombi", "jellyfin", "plex", "audiobookshelf", "lazylibrarian", ].some((app) => c.name.toLowerCase().includes(app)) ), download: containers.filter((c) => [ "qbittorrent", "transmission", "sabnzbd", "nzbget", "deluge", "gluetun", "flaresolverr", ].some((app) => c.name.toLowerCase().includes(app)) ), infrastructure: containers.filter((c) => [ "traefik", "portainer", "heimdall", "homepage", "nginx", "caddy", "pihole", "adguard", "unbound", "mosquitto", ].some((app) => c.name.toLowerCase().includes(app)) ), monitoring: containers.filter((c) => [ "grafana", "prometheus", "cadvisor", "node-exporter", "dozzle", "uptime-kuma", "beszel", "dockmon", "docker-stats-exporter", "diun", "container-census", ].some((app) => c.name.toLowerCase().includes(app)) ), automation: containers.filter((c) => [ "homeassistant", "home-assistant", "n8n", "nodered", "node-red", "duplicati", ].some((app) => c.name.toLowerCase().includes(app)) ), productivity: containers.filter((c) => [ "nextcloud", "openproject", "gitea", "gitlab", "code-server", "vscode", ].some((app) => c.name.toLowerCase().includes(app)) ), media_processing: containers.filter((c) => ["tdarr"].some((app) => c.name.toLowerCase().includes(app)) ), ai: containers.filter((c) => ["openwebui", "open-webui", "ollama", "stable-diffusion", "mcp"].some( (app) => c.name.toLowerCase().includes(app) ) ), photos: containers.filter((c) => ["immich"].some((app) => c.name.toLowerCase().includes(app)) ), databases: containers.filter((c) => ["postgres", "mariadb", "mysql", "mongo", "redis", "db"].some((app) => c.name.toLowerCase().includes(app) ) ), }; }; const filteredContainers = containers.filter( (c) => c.name.toLowerCase().includes(searchQuery.toLowerCase()) || c.image.toLowerCase().includes(searchQuery.toLowerCase()) ); const grouped = groupContainers( searchQuery ? filteredContainers : containers ); return (
{/* Header */}

Atlas Dashboard

{containers.length} containers
{/* Widgets Section */}
{/* Grafana Dashboards */}
{/* Container Groups */} {loading ? (
) : (
{grouped.media.length > 0 && ( )} {grouped.download.length > 0 && ( )} {grouped.ai.length > 0 && ( )} {grouped.photos.length > 0 && ( )} {grouped.media_processing.length > 0 && ( )} {grouped.automation.length > 0 && ( )} {grouped.productivity.length > 0 && ( )} {grouped.infrastructure.length > 0 && ( )} {grouped.monitoring.length > 0 && ( )} {grouped.databases.length > 0 && ( )}
)}
); }