diff --git a/ui/src/components/ui/progress.tsx b/ui/src/components/ui/progress.tsx new file mode 100644 index 00000000..ed0fffe3 --- /dev/null +++ b/ui/src/components/ui/progress.tsx @@ -0,0 +1,25 @@ +'use client'; + +import * as React from 'react'; +import * as ProgressPrimitive from '@radix-ui/react-progress'; + +import { cn } from '@/lib/utils'; + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)); +Progress.displayName = ProgressPrimitive.Root.displayName; + +export { Progress }; diff --git a/ui/src/pages/home.tsx b/ui/src/pages/home.tsx index ae05f753..2d6584a4 100644 --- a/ui/src/pages/home.tsx +++ b/ui/src/pages/home.tsx @@ -1,140 +1,150 @@ +import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Card, CardContent } from '@/components/ui/card'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Skeleton } from '@/components/ui/skeleton'; import { Badge } from '@/components/ui/badge'; +import { Progress } from '@/components/ui/progress'; import { Key, Zap, Users, - Search, Stethoscope, AlertTriangle, - Terminal, ArrowRight, - Command, + Activity, + Clock, + CheckCircle2, + XCircle, + AlertCircle, + Cpu, + Database, + Settings, + Terminal, Sparkles, + Circle, } from 'lucide-react'; +import { CcsLogo } from '@/components/ccs-logo'; import { useOverview } from '@/hooks/use-overview'; import { useSharedSummary } from '@/hooks/use-shared'; +import { useHealth } from '@/hooks/use-health'; +import { useCliproxyAuth } from '@/hooks/use-cliproxy'; import { cn } from '@/lib/utils'; -const HEALTH_STATUS = { - ok: { label: 'Operational', class: 'bg-emerald-500/20 text-emerald-500 border-emerald-500/30' }, - warning: { label: 'Degraded', class: 'bg-amber-500/20 text-amber-500 border-amber-500/30' }, - error: { label: 'Issues', class: 'bg-red-500/20 text-red-500 border-red-500/30' }, -} as const; +// Live clock component +function LiveClock() { + const [time, setTime] = useState(new Date()); -// Three Pillars configuration - matches README branding -const THREE_PILLARS = [ + useEffect(() => { + const timer = setInterval(() => setTime(new Date()), 1000); + return () => clearInterval(timer); + }, []); + + return ( +
+ {time.toLocaleTimeString('en-US', { hour12: false })} +
+ ); +} + +// Provider status configuration +const PROVIDERS = [ + { id: 'gemini', name: 'Gemini', icon: '◆', color: 'bg-blue-500' }, + { id: 'codex', name: 'Codex', icon: '⬡', color: 'bg-green-500' }, + { id: 'agy', name: 'Antigravity', icon: '◇', color: 'bg-purple-500' }, +] as const; + +// Quick launch shortcuts - icon based +const QUICK_LAUNCH = [ { - id: 'api', - title: 'API Keys', - description: 'Configure GLM, Kimi with your own API keys', - icon: Key, - path: '/api', - gradient: 'from-orange-500/20 to-amber-500/10', - iconBg: 'bg-orange-500/20 text-orange-500', + key: 'G', + label: 'Gemini', + command: 'ccs gemini', + color: 'hover:bg-blue-500/20 hover:border-blue-500/50', }, { - id: 'oauth', - title: 'OAuth Providers', - description: 'Gemini, Codex, Antigravity - zero config', - icon: Zap, - path: '/cliproxy', - gradient: 'from-blue-500/20 to-cyan-500/10', - iconBg: 'bg-blue-500/20 text-blue-500', + key: 'C', + label: 'Codex', + command: 'ccs codex', + color: 'hover:bg-green-500/20 hover:border-green-500/50', }, { - id: 'accounts', - title: 'Multiple Accounts', - description: 'Isolated Claude instances for work & personal', - icon: Users, - path: '/accounts', - gradient: 'from-violet-500/20 to-purple-500/10', - iconBg: 'bg-violet-500/20 text-violet-500', + key: 'A', + label: 'Agy', + command: 'ccs agy', + color: 'hover:bg-purple-500/20 hover:border-purple-500/50', + }, + { + key: 'K', + label: 'Kimi', + command: 'ccs kimi', + color: 'hover:bg-amber-500/20 hover:border-amber-500/50', + }, + { + key: 'L', + label: 'GLM', + command: 'ccs glm', + color: 'hover:bg-orange-500/20 hover:border-orange-500/50', + }, + { + key: 'D', + label: 'Default', + command: 'ccs', + color: 'hover:bg-primary/20 hover:border-primary/50', }, ]; -// Quick launch shortcuts -const QUICK_LAUNCH = [ - { key: 'G', label: 'Gemini', command: 'ccs gemini' }, - { key: 'C', label: 'Codex', command: 'ccs codex' }, - { key: 'A', label: 'Agy', command: 'ccs agy' }, +// System status items +const SYSTEM_ITEMS = [ + { id: 'api', label: 'API Keys', icon: Key, path: '/api' }, + { id: 'oauth', label: 'OAuth', icon: Zap, path: '/cliproxy' }, + { id: 'accounts', label: 'Accounts', icon: Users, path: '/accounts' }, + { id: 'health', label: 'Doctor', icon: Stethoscope, path: '/health' }, + { id: 'settings', label: 'Settings', icon: Settings, path: '/settings' }, + { id: 'shared', label: 'Shared', icon: Database, path: '/shared' }, ]; export function HomePage() { const navigate = useNavigate(); const { data: overview, isLoading: isOverviewLoading } = useOverview(); const { data: shared, isLoading: isSharedLoading } = useSharedSummary(); + const { data: health, isLoading: isHealthLoading } = useHealth(); + const { data: cliproxyAuth } = useCliproxyAuth(); - if (isOverviewLoading || isSharedLoading) { + if (isOverviewLoading || isSharedLoading || isHealthLoading) { return ; } - const healthInfo = overview?.health?.status - ? HEALTH_STATUS[overview.health.status as keyof typeof HEALTH_STATUS] - : HEALTH_STATUS.ok; + const healthPercent = health?.summary + ? Math.round((health.summary.passed / health.summary.total) * 100) + : 0; + const healthStatus = overview?.health?.status ?? 'ok'; - const pillarCounts = { - api: overview?.profiles ?? 0, - oauth: overview?.cliproxy ?? 0, - accounts: overview?.accounts ?? 0, - }; + // Build provider status from auth data + const providerStatus = PROVIDERS.map((provider) => { + const authInfo = cliproxyAuth?.authStatus?.find((a) => a.provider === provider.id); + return { + ...provider, + authenticated: authInfo?.authenticated ?? false, + accounts: authInfo?.accounts?.length ?? 0, + }; + }); return ( -
- {/* Gradient Mesh Background */} -
-
-
+
+ {/* Subtle grid background */} +
+
-
- {/* Hero Header */} -
- - -
-
-
-
- -
-
-

CCS

-

Multi-Account AI Management

-
-
-
-
- - v{overview?.version ?? '0.0.0'} - - - - {healthInfo.label} - -
-
- - {/* Command Palette Search Bar */} -
navigate('/health')}> -
- - - Search commands, profiles, or run diagnostics... - - - K - -
-
-
-
-
- - {/* Configuration Warning */} +
+ {/* Configuration Warning - Full Width */} {shared?.symlinkStatus && !shared.symlinkStatus.valid && ( )} - {/* Three Pillars Section */} -
-
-

The Three Pillars

- — What do you want to do? -
-
- {THREE_PILLARS.map((pillar) => { - const Icon = pillar.icon; - const count = pillarCounts[pillar.id as keyof typeof pillarCounts]; - return ( - navigate(pillar.path)} - > - -
-
- -
- {count} -
-
-

- {pillar.title} -

-

- {pillar.description} -

-
-
- Configure - -
-
-
- ); - })} -
-
+ {/* Main Status Board Grid - Full Width */} +
+ {/* Left Column: Hero + System Health */} +
+ {/* Hero Card with Logo */} + + +
+ +
+

CCS

+

Control Center

+
+ + v{overview?.version ?? '0.0.0'} + +
- {/* Quick Launch & Health Grid */} -
- {/* Quick Launch */} - - -
-

- - Quick Launch -

- Click to copy command -
-
- {QUICK_LAUNCH.map((item) => ( -
+ + {/* Center Column: Provider Status + Quick Launch */} +
+ {/* Provider Status Board */} + + +
+

+ + Provider Status +

+ - ))} - {/* Account shortcuts */} - {overview?.accounts && overview.accounts > 0 && ( - <> -
+ +
+ {providerStatus.map((provider) => ( +
-
- + {/* Status Indicator */} +
+ + {provider.authenticated && ( + + + + )}
- - Accounts + + {/* Provider Info */} +
+
+ {provider.name} + {provider.authenticated && ( + + {provider.accounts} {provider.accounts === 1 ? 'account' : 'accounts'} + + )} +
+

+ {provider.authenticated ? 'Connected' : 'Not authenticated'} +

+
+ + {/* Status Bar */} +
+ [role=progressbar]]:bg-emerald-500' + )} + /> +
+
+ ))} +
+ + + + {/* Quick Launch Grid */} + + +
+

+ + Quick Launch +

+ Click to copy +
+ +
+ {QUICK_LAUNCH.map((item) => ( + - - )} - {/* Doctor shortcut */} - -
-
-
- - {/* Health & Resources */} - - - {/* Health Summary */} -
-

- System Health -

-
navigate('/health')} - > -
-
- - {overview?.health?.passed ?? 0}/{overview?.health?.total ?? 0} - -
- checks passed + ))}
-
+ + +
- {/* Resources Summary */} -
-

- Shared Resources + {/* Right Column: Navigation + Resources */} +
+ {/* System Navigation */} + + +

+ + Navigation

-
navigate('/shared')} - > -
- + +
+ {SYSTEM_ITEMS.map((item) => { + const Icon = item.icon; + return ( + + ); + })} +
+ + + + {/* Shared Resources */} + navigate('/shared')} + > + +
+

+ + Shared Resources +

+ +
+ +
+
+

{shared?.commands ?? 0} - - cmds +

+

+ Commands +

-
- +
+

{shared?.skills ?? 0} - - skills +

+

+ Skills +

-
- +
+

{shared?.agents ?? 0} - - agents +

+

+ Agents +

-
- - + + + + {/* Uptime Indicator */} + + +
+
+
+
+
+
+

System Online

+

+ + Config server running +

+
+
+ + +
); } -// Skeleton loader component +// Skeleton loader component - Full Width function HomePageSkeleton() { return ( -
- {/* Hero Skeleton */} -
-
- -
- - -
-
- -
- - {/* Pillars Skeleton */} -
- {[1, 2, 3].map((i) => ( -
-
- - +
+
+ {/* Left Column */} +
+
+
+ +
+ + +
+
+ +
+ {[1, 2, 3].map((i) => ( + + ))}
- -
- ))} -
- - {/* Quick Launch Skeleton */} -
-
- -
- {[1, 2, 3, 4, 5, 6].map((i) => ( - - ))} +
+ + +
-
- - - -
+ + {/* Center Column */} +
+
+ {[1, 2, 3].map((i) => ( - + ))}
+
+ +
+ {[1, 2, 3, 4, 5, 6].map((i) => ( + + ))} +
+
+
+ + {/* Right Column */} +
+
+ + {[1, 2, 3, 4, 5, 6].map((i) => ( + + ))} +
+
+ +
+ {[1, 2, 3].map((i) => ( + + ))} +
+