Template
fix
This commit is contained in:
1 parent
61df5e3dcc
commit
c0a94d3b6d
25 files changed
+2143
-348
No files matched your search
@@ -0,0 +1,149 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '../ui/Table'
|
||||
import { Badge } from '../ui/Badge'
|
||||
import { Kbd } from '../ui/Kbd'
|
||||
import { EmptyState, EmptyStateIcon, EmptyStateTitle, EmptyStateDescription } from '../ui/EmptyState'
|
||||
import { Icon } from '../Icon'
|
||||
|
||||
type Service = {
|
||||
id: string
|
||||
name: string
|
||||
region: string
|
||||
status: 'healthy' | 'degraded' | 'scaling'
|
||||
latency: string
|
||||
memory: string
|
||||
instances: number
|
||||
}
|
||||
|
||||
const INITIAL_SERVICES: Service[] = [
|
||||
{ id: '1', name: 'api-gateway', region: 'us-east-1', status: 'healthy', latency: '12ms', memory: '128 MB', instances: 8 },
|
||||
{ id: '2', name: 'vector-indexer', region: 'eu-central-1', status: 'healthy', latency: '34ms', memory: '890 MB', instances: 4 },
|
||||
{ id: '3', name: 'model-inference', region: 'us-west-2', status: 'scaling', latency: '78ms', memory: '4.2 GB', instances: 16 },
|
||||
{ id: '4', name: 'session-broker', region: 'us-east-1', status: 'healthy', latency: '8ms', memory: '64 MB', instances: 6 },
|
||||
{ id: '5', name: 'analytics-worker', region: 'ap-northeast-1', status: 'degraded', latency: '142ms', memory: '1.8 GB', instances: 2 },
|
||||
{ id: '6', name: 'audit-logger', region: 'eu-west-1', status: 'healthy', latency: '19ms', memory: '210 MB', instances: 3 },
|
||||
]
|
||||
|
||||
export function ServiceTable() {
|
||||
const [search, setSearch] = useState('')
|
||||
const [filterStatus, setFilterStatus] = useState<string>('all')
|
||||
|
||||
const filteredServices = useMemo(() => {
|
||||
return INITIAL_SERVICES.filter(svc => {
|
||||
const matchesSearch = svc.name.toLowerCase().includes(search.toLowerCase()) ||
|
||||
svc.region.toLowerCase().includes(search.toLowerCase())
|
||||
const matchesStatus = filterStatus === 'all' || svc.status === filterStatus
|
||||
return matchesSearch && matchesStatus
|
||||
})
|
||||
}, [search, filterStatus])
|
||||
|
||||
return (
|
||||
<div className="stack-md">
|
||||
{/* Table Toolbar */}
|
||||
<div className="flex-between" style={{ flexWrap: 'wrap', gap: 'var(--space-3)' }}>
|
||||
<div className="icon-search-bar" style={{ maxWidth: 320 }}>
|
||||
<Icon name="search" size={15} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter services or regions..."
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
className="icon-search-input"
|
||||
/>
|
||||
{search && (
|
||||
<button className="icon-clear-btn" onClick={() => setSearch('')}>
|
||||
<Icon name="close" size={13} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="row-xs">
|
||||
{['all', 'healthy', 'scaling', 'degraded'].map(st => (
|
||||
<button
|
||||
key={st}
|
||||
className={`button ${filterStatus === st ? 'secondary' : 'ghost'}`}
|
||||
style={{
|
||||
padding: 'var(--space-1) var(--space-3)',
|
||||
fontSize: 'var(--text-xs)',
|
||||
textTransform: 'capitalize',
|
||||
}}
|
||||
onClick={() => setFilterStatus(st)}
|
||||
>
|
||||
{st}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Surface */}
|
||||
{filteredServices.length > 0 ? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Service Name</TableHead>
|
||||
<TableHead>Region</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>P99 Latency</TableHead>
|
||||
<TableHead>Memory</TableHead>
|
||||
<TableHead>Nodes</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredServices.map(svc => (
|
||||
<TableRow key={svc.id}>
|
||||
<TableCell style={{ fontWeight: 600 }}>
|
||||
<div className="row-xs" style={{ alignItems: 'center' }}>
|
||||
<span
|
||||
style={{
|
||||
width: 7,
|
||||
height: 7,
|
||||
borderRadius: '50%',
|
||||
background: svc.status === 'healthy' ? 'var(--chart-2)' : svc.status === 'scaling' ? 'var(--primary)' : 'var(--destructive)',
|
||||
boxShadow: `0 0 8px ${svc.status === 'healthy' ? 'var(--chart-2)' : svc.status === 'scaling' ? 'var(--primary)' : 'var(--destructive)'}`,
|
||||
}}
|
||||
/>
|
||||
{svc.name}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Kbd>{svc.region}</Kbd>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant={svc.status === 'healthy' ? 'positive' : svc.status === 'scaling' ? 'primary' : 'destructive'}
|
||||
>
|
||||
{svc.status}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="mono" style={{ color: svc.status === 'degraded' ? 'var(--destructive)' : 'var(--primary)' }}>
|
||||
{svc.latency}
|
||||
</TableCell>
|
||||
<TableCell className="mono" style={{ color: 'var(--muted-foreground)' }}>
|
||||
{svc.memory}
|
||||
</TableCell>
|
||||
<TableCell className="mono" style={{ color: 'var(--foreground)' }}>
|
||||
{svc.instances}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<EmptyState>
|
||||
<EmptyStateIcon>
|
||||
<Icon name="search" size={24} />
|
||||
</EmptyStateIcon>
|
||||
<EmptyStateTitle>No matching services</EmptyStateTitle>
|
||||
<EmptyStateDescription>
|
||||
No active nodes or services match the current filter query "{search}".
|
||||
</EmptyStateDescription>
|
||||
</EmptyState>
|
||||
)}
|
||||
|
||||
<div className="flex-between muted" style={{ fontSize: 'var(--text-3xs)', padding: '0 var(--space-2)' }}>
|
||||
<span>Showing {filteredServices.length} of {INITIAL_SERVICES.length} deployed services</span>
|
||||
<span className="mono">Synced live via cluster coordinator</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user