mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-08 01:43:55 +00:00
feat: sortable resource tables and richer dashboard stack-health columns (#1498)
Make the Resources Images, Volumes, and Networks tables sortable with a shared useTableSort hook and SortableTableHead, move the Images scan-history control into the Images tab header, and keep the network List/Topology toggle anchored with Create Network visible in both modes. Rework the dashboard Stack health table: drop the redundant Host column, add sortable Stack/Up/CPU/Mem headers, and add Source (local/git) and Port columns. The status endpoint now labels each stack with its git/local source, computed outside the cache so linking changes show immediately. Extract a reusable CreateNetworkDialog and add a create-network action to the stack-detail Networking tab.
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Network, Globe, Lock, ShieldQuestion, RefreshCw, ArrowRight } from 'lucide-react';
|
||||
import { Network, Globe, Lock, ShieldQuestion, RefreshCw, ArrowRight, Plus } from 'lucide-react';
|
||||
import { apiFetch } from '@/lib/api';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { toast } from '@/components/ui/toast-store';
|
||||
import { useNodes } from '@/context/NodeContext';
|
||||
import { CreateNetworkDialog } from '@/components/resources/CreateNetworkDialog';
|
||||
|
||||
// Mirrors the backend networking payload shapes (the frontend never imports
|
||||
// backend). IntentEntry intentionally keeps only the fields this panel reads.
|
||||
@@ -110,6 +111,7 @@ export default function StackNetworkingPanel({ stackName, canEdit, doctorEnabled
|
||||
const [loadError, setLoadError] = useState(false);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [reloadKey, setReloadKey] = useState(0);
|
||||
const [showCreateNetwork, setShowCreateNetwork] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -197,11 +199,24 @@ export default function StackNetworkingPanel({ stackName, canEdit, doctorEnabled
|
||||
<div data-testid="networking-panel" className="flex-1 min-h-0 overflow-y-auto px-3 py-3 flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className={LABEL_CLASS}>networking</span>
|
||||
<button type="button" onClick={() => setReloadKey(k => k + 1)} disabled={refreshing} className={ACTION_CLASS}>
|
||||
<RefreshCw className={cn('h-3 w-3', refreshing && 'animate-spin')} strokeWidth={1.5} /> refresh
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
{canEdit && (
|
||||
<button type="button" onClick={() => setShowCreateNetwork(true)} className={ACTION_CLASS}>
|
||||
<Plus className="h-3 w-3" strokeWidth={1.5} /> create network
|
||||
</button>
|
||||
)}
|
||||
<button type="button" onClick={() => setReloadKey(k => k + 1)} disabled={refreshing} className={ACTION_CLASS}>
|
||||
<RefreshCw className={cn('h-3 w-3', refreshing && 'animate-spin')} strokeWidth={1.5} /> refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CreateNetworkDialog
|
||||
open={showCreateNetwork}
|
||||
onOpenChange={setShowCreateNetwork}
|
||||
onCreated={() => setReloadKey(k => k + 1)}
|
||||
/>
|
||||
|
||||
{/* Exposure intent */}
|
||||
<section className="flex flex-col gap-2">
|
||||
<div className={LABEL_CLASS}>exposure intent</div>
|
||||
|
||||
Reference in New Issue
Block a user