feat: Remote Nodes Foundation (Strategy B) - Add nodes table with auto-seeded default local node in DatabaseService - Create NodeRegistry service for multi-instance Docker daemon connections - Add 6 Node management API endpoints (CRUD + test connection) - Create NodeManager component with table UI and connection testing - Add NodeContext for frontend-wide active node state management - Add node switcher dropdown to sidebar (visible when >1 node) - Add Nodes tab to Settings Hub

This commit is contained in:
SaelixCode
2026-03-18 11:45:04 -04:00
parent c324d987ea
commit 02e1ebe1b6
9 changed files with 929 additions and 6 deletions
+16 -3
View File
@@ -12,7 +12,8 @@ import { Slider } from '@/components/ui/slider';
import { toast } from 'sonner';
import { apiFetch } from '@/lib/api';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Shield, Activity, Bell, Palette, Moon, Sun, Code } from 'lucide-react';
import { Shield, Activity, Bell, Palette, Moon, Sun, Code, Server } from 'lucide-react';
import { NodeManager } from './NodeManager';
interface Agent {
type: 'discord' | 'slack' | 'webhook';
@@ -28,7 +29,7 @@ interface SettingsModalProps {
}
export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: SettingsModalProps) {
const [activeSection, setActiveSection] = useState<'account' | 'system' | 'notifications' | 'appearance' | 'developer'>('account');
const [activeSection, setActiveSection] = useState<'account' | 'system' | 'notifications' | 'appearance' | 'developer' | 'nodes'>('account');
// Auth State
const [authData, setAuthData] = useState({ oldPassword: '', newPassword: '', confirmPassword: '' });
@@ -221,7 +222,7 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-[800px] h-[600px] flex p-0 font-sans shadow-lg bg-background border-border overflow-hidden gap-0">
<DialogContent className="sm:max-w-[900px] h-[650px] flex p-0 font-sans shadow-lg bg-background border-border overflow-hidden gap-0">
{/* Sidebar */}
<div className="w-[200px] bg-muted/20 border-r border-border flex flex-col p-4 shrink-0">
<div className="font-semibold text-lg mb-6 text-foreground tracking-tight">Settings Hub</div>
@@ -266,6 +267,14 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
<Code className="w-4 h-4 mr-2" />
Developer
</Button>
<Button
variant={activeSection === 'nodes' ? 'secondary' : 'ghost'}
className="w-full justify-start font-medium"
onClick={() => setActiveSection('nodes')}
>
<Server className="w-4 h-4 mr-2" />
Nodes
</Button>
</nav>
</div>
@@ -478,6 +487,10 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
</div>
)}
{activeSection === 'nodes' && (
<NodeManager />
)}
</div>
</DialogContent>
</Dialog>