Skip to content

Commit 04c20f2

Browse files
committed
fix(frontend): apply missing UI redesign changes from #132
Include provider-neutral theming, GPU fit indicator redesign, animation fixes, and Headlamp plugin cleanup that were left unstaged in #132. - Remove provider-specific color/theming (getProviderBadgeClass, getProviderDisplayName, useProviderTheme, providerColors, [data-provider] CSS) - Redesign GPU fit indicator with four-tier llmfit-inspired system (perfect/good/tight/won't fit) with upgrade delta display - Fix animation timing: replace invalid ease-out-expo with cubic-bezier - Update sidebar to fixed 240px with cyan active pill indicator - Replace Card components with glass-panel styling across pages - Remove getRuntimeColors/getRuntimeDescription from Headlamp plugin
1 parent 4da540e commit 04c20f2

17 files changed

Lines changed: 329 additions & 302 deletions

frontend/src/components/deployments/DeploymentList.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,24 @@ interface DeploymentListProps {
2323
isLoading?: boolean
2424
}
2525

26-
function getProviderBadgeClass(provider: string): string {
27-
switch (provider) {
28-
case 'kuberay': return 'bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300'
29-
case 'kaito': return 'bg-purple-100 text-purple-700 dark:bg-purple-950 dark:text-purple-300'
30-
case 'llmd': return 'bg-orange-100 text-orange-700 dark:bg-orange-950 dark:text-orange-300'
31-
default: return 'bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300'
26+
function getStatusDotColor(phase: DeploymentStatus['phase']): string {
27+
switch (phase) {
28+
case 'Running': return 'bg-green-500'
29+
case 'Pending': return 'bg-amber-400 animate-pulse'
30+
case 'Deploying': return 'bg-blue-500 animate-pulse'
31+
case 'Failed': return 'bg-red-400'
32+
case 'Terminating': return 'bg-slate-400 animate-pulse'
33+
default: return 'bg-slate-500'
3234
}
3335
}
3436

35-
function getProviderDisplayName(provider: string): string {
36-
switch (provider) {
37-
case 'kuberay': return 'KubeRay'
38-
case 'kaito': return 'KAITO'
39-
case 'llmd': return 'llm-d'
40-
case 'dynamo': return 'Dynamo'
41-
default: return provider
37+
function getReplicaColorClass(deployment: DeploymentStatus): string {
38+
if (deployment.mode === 'disaggregated' && deployment.prefillReplicas && deployment.decodeReplicas) {
39+
const allReady = deployment.prefillReplicas.ready === deployment.prefillReplicas.desired &&
40+
deployment.decodeReplicas.ready === deployment.decodeReplicas.desired
41+
return allReady ? 'text-green-400' : 'text-amber-400'
4242
}
43+
return deployment.replicas.ready === deployment.replicas.desired ? 'text-green-400' : 'text-amber-400'
4344
}
4445

4546
/**
@@ -112,8 +113,8 @@ export function DeploymentList({ deployments, isLoading }: DeploymentListProps)
112113
{deployments.map((deployment, index) => (
113114
<div
114115
key={deployment.name}
115-
className="rounded-lg border shadow-soft-sm p-4 space-y-3 bg-card"
116-
style={{ animationDelay: `${index * 50}ms` }}
116+
className="glass-panel !p-4 flex items-center gap-4 group hover:bg-white/5 hover:border-white/10 transition-all duration-200 animate-slide-up"
117+
style={{ animationDelay: `${Math.min(index, 12) * 50}ms`, animationFillMode: 'both' }}
117118
>
118119
{/* Header: Name and Status */}
119120
<div className="flex items-start justify-between gap-2">
@@ -138,9 +139,8 @@ export function DeploymentList({ deployments, isLoading }: DeploymentListProps)
138139
</Badge>
139140
<Badge
140141
variant="secondary"
141-
className={getProviderBadgeClass(deployment.provider)}
142142
>
143-
{getProviderDisplayName(deployment.provider)}
143+
{deployment.provider}
144144
</Badge>
145145
{deployment.mode === 'disaggregated' && (
146146
<Badge variant="secondary" className="text-xs">P/D</Badge>

frontend/src/components/layout/MainLayout.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ReactNode, useState, useCallback, useEffect, createContext, useContext } from 'react'
22
import { Sidebar } from './Sidebar'
33
import { Header } from './Header'
4-
import { useProviderTheme } from '@/hooks/useProviderTheme'
54

65
interface SidebarContextValue {
76
isOpen: boolean
@@ -25,9 +24,6 @@ interface MainLayoutProps {
2524
}
2625

2726
export function MainLayout({ children }: MainLayoutProps) {
28-
// Apply provider-specific theme colors
29-
useProviderTheme()
30-
3127
const [isSidebarOpen, setIsSidebarOpen] = useState(false)
3228

3329
const toggle = useCallback(() => {

frontend/src/components/layout/Sidebar.tsx

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,23 @@ export function Sidebar({ onNavigate }: SidebarProps) {
2222
}
2323

2424
return (
25-
<div className="flex h-full w-64 flex-col border-r bg-card overflow-hidden shadow-soft-sm md:shadow-none">
26-
{/* Header with logo */}
27-
<div className="flex h-16 items-center justify-between border-b px-4 md:px-6">
28-
<Link to="/" className="flex items-center gap-2" onClick={handleNavClick}>
29-
<img src="/logo.png" alt="KubeAIRunway" className="h-8 w-8" />
30-
<span className="text-xl font-bold text-foreground">KubeAIRunway</span>
25+
<div
26+
className={cn(
27+
'flex h-full w-60 flex-col bg-background border-r border-white/5 overflow-hidden',
28+
onNavigate && 'shadow-soft-sm'
29+
)}
30+
>
31+
{/* Logo */}
32+
<div className="flex h-14 items-center border-b border-white/5 px-4 shrink-0">
33+
<Link
34+
to="/"
35+
className="flex items-center gap-2 min-w-0"
36+
onClick={handleNavClick}
37+
>
38+
<img src="/logo.png" alt="KubeAIRunway" className="h-8 w-8 shrink-0" />
39+
<span className="text-xl font-bold text-foreground whitespace-nowrap">
40+
KubeAIRunway
41+
</span>
3142
</Link>
3243

3344
{/* Close button - mobile only */}
@@ -63,11 +74,23 @@ export function Sidebar({ onNavigate }: SidebarProps) {
6374
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground active:scale-[0.98]'
6475
)}
6576
>
66-
<item.icon className={cn(
67-
'h-5 w-5 transition-transform duration-150',
68-
isActive && 'scale-110'
69-
)} />
70-
{item.name}
77+
<span
78+
className={cn(
79+
'absolute left-0 w-1 h-8 rounded-full bg-primary transition-all duration-200 ease-out origin-center',
80+
isActive
81+
? 'opacity-100 scale-y-100'
82+
: 'opacity-0 scale-y-0'
83+
)}
84+
/>
85+
<item.icon
86+
className={cn(
87+
'h-5 w-5 shrink-0 transition-transform duration-150',
88+
isActive && 'scale-110'
89+
)}
90+
/>
91+
<span className="whitespace-nowrap text-slate-300">
92+
{item.name}
93+
</span>
7194
</Link>
7295
)
7396
})}

frontend/src/components/metrics/MetricCard.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,14 @@ export function MetricGrid({ metrics, className }: MetricGridProps) {
6464

6565
return (
6666
<div className={cn("grid gap-4 sm:grid-cols-2 lg:grid-cols-3", className)}>
67-
{metrics.map((metric) => (
68-
<MetricCard key={metric.name} metric={metric} />
67+
{metrics.map((metric, index) => (
68+
<div
69+
key={metric.name}
70+
className="animate-slide-up"
71+
style={{ animationDelay: `${Math.min(index, 12) * 50}ms`, animationFillMode: 'both' }}
72+
>
73+
<MetricCard metric={metric} />
74+
</div>
6975
))}
7076
</div>
7177
)

0 commit comments

Comments
 (0)