diff --git a/ui/src/components/ui/progress.tsx b/ui/src/components/ui/progress.tsx deleted file mode 100644 index ed0fffe3..00000000 --- a/ui/src/components/ui/progress.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'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 2d6584a4..31b2bcb7 100644 --- a/ui/src/pages/home.tsx +++ b/ui/src/pages/home.tsx @@ -1,544 +1,204 @@ -import { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Card, CardContent } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { StatCard } from '@/components/stat-card'; +import { HeroSection } from '@/components/hero-section'; +import { QuickCommands } from '@/components/quick-commands'; 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, + Activity, + Plus, Stethoscope, + BookOpen, + FolderOpen, AlertTriangle, ArrowRight, - 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'; -// Live clock component -function LiveClock() { - const [time, setTime] = useState(new Date()); - - 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 = [ - { - key: 'G', - label: 'Gemini', - command: 'ccs gemini', - color: 'hover:bg-blue-500/20 hover:border-blue-500/50', - }, - { - key: 'C', - label: 'Codex', - command: 'ccs codex', - color: 'hover:bg-green-500/20 hover:border-green-500/50', - }, - { - 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', - }, -]; - -// 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' }, -]; +const HEALTH_VARIANTS = { + ok: 'success', + warning: 'warning', + error: 'error', +} as const; 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 || isHealthLoading) { - return ; + if (isOverviewLoading || isSharedLoading) { + return ( +
+ {/* Hero Skeleton */} +
+
+ +
+ + +
+
+
+ + {/* Stats Skeleton */} +
+ {[1, 2, 3, 4].map((i) => ( +
+
+ +
+ + +
+
+
+ ))} +
+ + {/* Quick Actions Skeleton */} +
+ +
+ + + +
+
+ + {/* Quick Commands Skeleton */} +
+ +
+ {[1, 2, 3, 4].map((i) => ( + + ))} +
+
+
+ ); } - const healthPercent = health?.summary - ? Math.round((health.summary.passed / health.summary.total) * 100) - : 0; - const healthStatus = overview?.health?.status ?? 'ok'; - - // 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, - }; - }); + const healthVariant = overview?.health + ? HEALTH_VARIANTS[overview.health.status as keyof typeof HEALTH_VARIANTS] + : undefined; return ( -
- {/* Subtle grid background */} -
-
+ {/* Hero Section */} + + + {/* Configuration Warning */} + {shared?.symlinkStatus && !shared.symlinkStatus.valid && ( + + + Configuration Required + {shared.symlinkStatus.message} + + )} + + {/* Stats Grid */} +
+ navigate('/api')} + /> + navigate('/cliproxy')} + /> + navigate('/accounts')} + /> + navigate('/health')} />
-
- {/* Configuration Warning - Full Width */} - {shared?.symlinkStatus && !shared.symlinkStatus.valid && ( - + + Quick Actions + + + + + + + + + {/* Quick Commands */} + + + {/* Shared Data Summary */} + + + + + Shared Data + + + + +
+
+ {shared?.commands ?? 0} + Commands +
+
+ {shared?.skills ?? 0} + Skills +
+
+ {shared?.agents ?? 0} + Agents +
- - {/* Center Column: Provider Status + Quick Launch */} -
- {/* Provider Status Board */} - - -
-

- - Provider Status -

- -
- -
- {providerStatus.map((provider) => ( -
- {/* Status Indicator */} -
- - {provider.authenticated && ( - - - - )} -
- - {/* 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) => ( - - ))} -
-
-
-
- - {/* Right Column: Navigation + Resources */} -
- {/* System Navigation */} - - -

- - Navigation -

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

- - Shared Resources -

- -
- -
-
-

- {shared?.commands ?? 0} -

-

- Commands -

-
-
-

- {shared?.skills ?? 0} -

-

- Skills -

-
-
-

- {shared?.agents ?? 0} -

-

- Agents -

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

System Online

-

- - Config server running -

-
-
- - -
-
-
-
- ); -} - -// Skeleton loader component - Full Width -function HomePageSkeleton() { - return ( -
-
- {/* Left Column */} -
-
-
- -
- - -
-
- -
- {[1, 2, 3].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) => ( - - ))} -
-
-
-
+ +
); }