fix(ui): show OpenRouterQuickStart by default on API page

- remove auto-select of first profile behavior
- show QuickStart panel when no profile selected
- users now click profile to see details
- ensures OpenRouter promo visible for existing users
This commit is contained in:
kaitranntt committed 2025-12-20 19:49:12 -05:00
1 parent adcc3235f0
commit 05380e21b4
1 file changed
+34 -81
+34 -81
View File
@@ -7,22 +7,21 @@ import { useState, useMemo } from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
import { import {
Plus, Plus,
Search, Search,
Settings2,
Trash2, Trash2,
CheckCircle2, CheckCircle2,
AlertCircle, AlertCircle,
Server, Server,
ExternalLink,
FileJson, FileJson,
RefreshCw, RefreshCw,
} from 'lucide-react'; } from 'lucide-react';
import { ProfileEditor } from '@/components/profile-editor'; import { ProfileEditor } from '@/components/profile-editor';
import { ProfileCreateDialog } from '@/components/profiles/profile-create-dialog'; import { ProfileCreateDialog } from '@/components/profiles/profile-create-dialog';
import { OpenRouterBanner } from '@/components/profiles/openrouter-banner';
import { OpenRouterQuickStart } from '@/components/profiles/openrouter-quick-start';
import { OpenRouterPromoCard } from '@/components/profiles/openrouter-promo-card';
import { useProfiles, useDeleteProfile } from '@/hooks/use-profiles'; import { useProfiles, useDeleteProfile } from '@/hooks/use-profiles';
import { useOpenRouterModels } from '@/hooks/use-openrouter-models'; import { useOpenRouterModels } from '@/hooks/use-openrouter-models';
import { ConfirmDialog } from '@/components/shared/confirm-dialog'; import { ConfirmDialog } from '@/components/shared/confirm-dialog';
@@ -36,6 +35,7 @@ export function ApiPage() {
const [selectedProfile, setSelectedProfile] = useState<string | null>(null); const [selectedProfile, setSelectedProfile] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [isCreateDialogOpen, setCreateDialogOpen] = useState(false); const [isCreateDialogOpen, setCreateDialogOpen] = useState(false);
const [createMode, setCreateMode] = useState<'normal' | 'openrouter'>('normal');
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null); const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
// Prefetch OpenRouter models when page loads (lazy - won't block render) // Prefetch OpenRouter models when page loads (lazy - won't block render)
@@ -50,13 +50,11 @@ export function ApiPage() {
[profiles, searchQuery] [profiles, searchQuery]
); );
// Compute effective selected profile (auto-select first if none selected) // selectedProfile is null by default - user must click to select
const effectiveSelectedProfile = useMemo(() => { // This allows OpenRouterQuickStart to show as the default right panel
if (selectedProfile && profiles.some((p) => p.name === selectedProfile)) { const selectedProfileData = selectedProfile
return selectedProfile; ? profiles.find((p) => p.name === selectedProfile)
} : null;
return profiles.length > 0 ? profiles[0].name : null;
}, [selectedProfile, profiles]);
// Handle profile deletion // Handle profile deletion
const handleDelete = (name: string) => { const handleDelete = (name: string) => {
@@ -76,10 +74,13 @@ export function ApiPage() {
setSelectedProfile(name); setSelectedProfile(name);
}; };
const selectedProfileData = profiles.find((p) => p.name === effectiveSelectedProfile);
return ( return (
<div className="h-[calc(100vh-100px)] flex"> <div className="h-[calc(100vh-100px)] flex flex-col">
{/* OpenRouter Announcement Banner */}
<OpenRouterBanner onCreateClick={() => setCreateDialogOpen(true)} />
{/* Main Content */}
<div className="flex-1 flex min-h-0">
{/* Left Panel - Profiles List */} {/* Left Panel - Profiles List */}
<div className="w-80 border-r flex flex-col bg-muted/30"> <div className="w-80 border-r flex flex-col bg-muted/30">
{/* Header */} {/* Header */}
@@ -166,7 +167,7 @@ export function ApiPage() {
<ProfileListItem <ProfileListItem
key={profile.name} key={profile.name}
profile={profile} profile={profile}
isSelected={effectiveSelectedProfile === profile.name} isSelected={selectedProfile === profile.name}
onSelect={() => { onSelect={() => {
setSelectedProfile(profile.name); setSelectedProfile(profile.name);
}} }}
@@ -191,9 +192,17 @@ export function ApiPage() {
</div> </div>
</div> </div>
)} )}
{/* OpenRouter Promo - always visible */}
<OpenRouterPromoCard
onCreateClick={() => {
setCreateMode('openrouter');
setCreateDialogOpen(true);
}}
/>
</div> </div>
{/* Right Panel - Editor */} {/* Right Panel - Editor or QuickStart */}
<div className="flex-1 flex flex-col min-w-0"> <div className="flex-1 flex flex-col min-w-0">
{selectedProfileData ? ( {selectedProfileData ? (
<ProfileEditor <ProfileEditor
@@ -201,19 +210,26 @@ export function ApiPage() {
onDelete={() => setDeleteConfirm(selectedProfileData.name)} onDelete={() => setDeleteConfirm(selectedProfileData.name)}
/> />
) : ( ) : (
<EmptyState <OpenRouterQuickStart
onCreateClick={() => { onOpenRouterClick={() => {
setCreateMode('openrouter');
setCreateDialogOpen(true);
}}
onCustomClick={() => {
setCreateMode('normal');
setCreateDialogOpen(true); setCreateDialogOpen(true);
}} }}
/> />
)} )}
</div> </div>
</div>
{/* Create Dialog */} {/* Create Dialog */}
<ProfileCreateDialog <ProfileCreateDialog
open={isCreateDialogOpen} open={isCreateDialogOpen}
onOpenChange={setCreateDialogOpen} onOpenChange={setCreateDialogOpen}
onSuccess={handleCreateSuccess} onSuccess={handleCreateSuccess}
initialMode={createMode}
/> />
{/* Delete Confirmation */} {/* Delete Confirmation */}
@@ -289,66 +305,3 @@ function ProfileListItem({
</div> </div>
); );
} }
/** Empty state when no profile is selected */
function EmptyState({ onCreateClick }: { onCreateClick: () => void }) {
return (
<div className="flex-1 flex items-center justify-center bg-muted/20">
<div className="text-center max-w-md px-8">
<Settings2 className="w-16 h-16 mx-auto text-muted-foreground/30 mb-6" />
<h2 className="text-xl font-semibold mb-2">API Profile Manager</h2>
<p className="text-muted-foreground mb-6">
Configure custom API endpoints for Claude CLI. Connect to proxy services like copilot-api,
OpenRouter, or your own API backend.
</p>
<div className="space-y-3">
<Button onClick={onCreateClick} className="w-full">
<Plus className="w-4 h-4 mr-2" />
Create Your First Profile
</Button>
<Separator className="my-4" />
<div className="text-left space-y-2">
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
What you can configure:
</p>
<ul className="text-sm text-muted-foreground space-y-1.5">
<li className="flex items-start gap-2">
<Badge variant="outline" className="text-xs shrink-0 mt-0.5">
URL
</Badge>
<span>Custom API base URL endpoint</span>
</li>
<li className="flex items-start gap-2">
<Badge variant="outline" className="text-xs shrink-0 mt-0.5">
Auth
</Badge>
<span>API key or authentication token</span>
</li>
<li className="flex items-start gap-2">
<Badge variant="outline" className="text-xs shrink-0 mt-0.5">
Models
</Badge>
<span>Model mapping for Opus/Sonnet/Haiku</span>
</li>
</ul>
</div>
<div className="pt-4">
<a
href="https://github.com/kaitranntt/ccs#api-profiles"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-xs text-primary hover:underline"
>
Learn more about API profiles
<ExternalLink className="w-3 h-3 ml-1" />
</a>
</div>
</div>
</div>
</div>
);
}