feat(ui): Phase 57 - remote context UX (Option A) + network layer fixes

Task 1 - Network layer fixes:
- AppStoreView: replace raw fetch() with apiFetch() on /templates and
  /templates/deploy so x-node-id header is injected for proxy routing
- GlobalObservabilityView: replace raw fetch() with apiFetch() on
  /stacks and /logs/global
- HostConsole: append ?nodeId= to WebSocket URL so the upgrade handler
  correctly routes the PTY session to the active remote node

Task 2 - Scoped context two-tier UX (Option A / Portainer-style):
- EditorLayout: add a context pill in the top header bar showing the
  active node name (pulsing blue for remote, green for local)
- HostConsole: header now reads "Host Console - [Node Name]"
- ResourcesView: title now reads "Resources Hub - [Node Name]" for remote
- GlobalObservabilityView: floating node badge in top-left corner for remote
- AppStoreView: deploy sheet shows "Deploying to: [Node Name]" badge
- SettingsModal: remote nodes only see System Limits + Developer tabs;
  global-only tabs (Account, Appearance, Notifications, Nodes) are hidden;
  header subtitle shows remote node name
This commit is contained in:
SaelixCode
2026-03-19 20:39:57 -04:00
parent e027a94492
commit 04c770c198
7 changed files with 117 additions and 40 deletions
@@ -8,6 +8,7 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
import { apiFetch } from '@/lib/api';
import { toast } from 'sonner';
import { Trash2, HardDrive, Network, PackageMinus, MonitorX, PieChart as ChartIcon } from 'lucide-react';
import { useNodes } from '@/context/NodeContext';
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts';
import { formatBytes } from '@/lib/utils';
@@ -42,6 +43,7 @@ interface OrphanContainer {
}
export default function ResourcesView() {
const { activeNode } = useNodes();
const [usage, setUsage] = useState<UsageData | null>(null);
const [images, setImages] = useState<DockerImage[]>([]);
const [volumes, setVolumes] = useState<DockerVolume[]>([]);
@@ -188,6 +190,9 @@ export default function ResourcesView() {
<div className="flex items-center gap-2">
<HardDrive className="w-6 h-6" />
<h1 className="text-2xl font-bold">Resources Hub</h1>
{activeNode?.type === 'remote' && (
<span className="text-sm font-normal text-muted-foreground"> {activeNode.name}</span>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">