Skip to content

Commit 8f5c006

Browse files
committed
feat(ui): simplify CLIProxy page UX with dedicated Add Account dialog
- Remove redundant "Create Variant" button (Quick Setup is more comprehensive) - Add AddAccountDialog component for per-provider account addition - Keep "Quick Setup" for full variant creation wizard - Keep "Add Account" per-row for adding accounts only
1 parent d868dc4 commit 8f5c006

File tree

2 files changed

+124
-34
lines changed

2 files changed

+124
-34
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Add Account Dialog Component
3+
* Simple dialog to add another OAuth account to a provider
4+
*
5+
* Shows auth command + refresh button (no variant creation)
6+
*/
7+
8+
import { useState } from 'react';
9+
import {
10+
Dialog,
11+
DialogContent,
12+
DialogHeader,
13+
DialogTitle,
14+
DialogDescription,
15+
} from '@/components/ui/dialog';
16+
import { Button } from '@/components/ui/button';
17+
import { Card, CardContent } from '@/components/ui/card';
18+
import { Copy, Check, RefreshCw, Terminal } from 'lucide-react';
19+
import { useCliproxyAuth } from '@/hooks/use-cliproxy';
20+
21+
interface AddAccountDialogProps {
22+
open: boolean;
23+
onClose: () => void;
24+
provider: string;
25+
displayName: string;
26+
}
27+
28+
export function AddAccountDialog({ open, onClose, provider, displayName }: AddAccountDialogProps) {
29+
const [copied, setCopied] = useState(false);
30+
const [isRefreshing, setIsRefreshing] = useState(false);
31+
const { refetch } = useCliproxyAuth();
32+
33+
const authCommand = `ccs ${provider} --auth --add`;
34+
35+
const copyCommand = async () => {
36+
await navigator.clipboard.writeText(authCommand);
37+
setCopied(true);
38+
setTimeout(() => setCopied(false), 2000);
39+
};
40+
41+
const handleRefresh = async () => {
42+
setIsRefreshing(true);
43+
await refetch();
44+
setIsRefreshing(false);
45+
onClose();
46+
};
47+
48+
return (
49+
<Dialog open={open} onOpenChange={onClose}>
50+
<DialogContent className="sm:max-w-md">
51+
<DialogHeader>
52+
<DialogTitle>Add {displayName} Account</DialogTitle>
53+
<DialogDescription>
54+
Run the command below in your terminal to authenticate a new account
55+
</DialogDescription>
56+
</DialogHeader>
57+
58+
<div className="space-y-4 py-4">
59+
<Card>
60+
<CardContent className="p-4 space-y-3">
61+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
62+
<Terminal className="w-4 h-4" />
63+
Run this command:
64+
</div>
65+
<div className="flex items-center gap-2">
66+
<code className="flex-1 px-3 py-2 bg-muted rounded-md font-mono text-sm">
67+
{authCommand}
68+
</code>
69+
<Button variant="outline" size="icon" onClick={copyCommand}>
70+
{copied ? (
71+
<Check className="w-4 h-4 text-green-500" />
72+
) : (
73+
<Copy className="w-4 h-4" />
74+
)}
75+
</Button>
76+
</div>
77+
<div className="text-xs text-muted-foreground">
78+
This will open your browser to authenticate with {displayName}
79+
</div>
80+
</CardContent>
81+
</Card>
82+
83+
<div className="flex items-center justify-end gap-2">
84+
<Button variant="ghost" onClick={onClose}>
85+
Cancel
86+
</Button>
87+
<Button onClick={handleRefresh} disabled={isRefreshing}>
88+
<RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />
89+
{isRefreshing ? 'Checking...' : 'I ran the command'}
90+
</Button>
91+
</div>
92+
</div>
93+
</DialogContent>
94+
</Dialog>
95+
);
96+
}

ui/src/pages/cliproxy.tsx

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {
1616
} from '@/components/ui/dropdown-menu';
1717
import { Plus, Check, X, User, ChevronDown, Star, Trash2, Sparkles } from 'lucide-react';
1818
import { CliproxyTable } from '@/components/cliproxy-table';
19-
import { CliproxyDialog } from '@/components/cliproxy-dialog';
2019
import { QuickSetupWizard } from '@/components/quick-setup-wizard';
20+
import { AddAccountDialog } from '@/components/add-account-dialog';
2121
import {
2222
useCliproxy,
2323
useCliproxyAuth,
@@ -85,10 +85,12 @@ function ProviderRow({
8585
status,
8686
setDefaultMutation,
8787
removeMutation,
88+
onAddAccount,
8889
}: {
8990
status: AuthStatus;
9091
setDefaultMutation: ReturnType<typeof useSetDefaultAccount>;
9192
removeMutation: ReturnType<typeof useRemoveAccount>;
93+
onAddAccount: () => void;
9294
}) {
9395
const accounts = status.accounts || [];
9496

@@ -148,35 +150,22 @@ function ProviderRow({
148150
</div>
149151

150152
<div className="flex items-center gap-2">
151-
{!status.authenticated && (
152-
<div className="text-xs font-mono bg-muted px-2 py-1 rounded text-muted-foreground select-all">
153-
ccs {status.provider} --auth
154-
</div>
155-
)}
156-
{status.authenticated && (
157-
<Button
158-
variant="outline"
159-
size="sm"
160-
className="h-7 text-xs gap-1"
161-
onClick={() => {
162-
// This is a placeholder since we can't actually run the auth command from UI easily without a terminal
163-
// But we can show the command to run
164-
navigator.clipboard.writeText(`ccs ${status.provider} --auth`);
165-
}}
166-
title="Copy auth command"
167-
>
168-
<Plus className="w-3 h-3" />
169-
Add Account
170-
</Button>
171-
)}
153+
{/* Show Add Account button for all - opens dialog with instructions */}
154+
<Button variant="outline" size="sm" className="h-7 text-xs gap-1" onClick={onAddAccount}>
155+
<Plus className="w-3 h-3" />
156+
Add Account
157+
</Button>
172158
</div>
173159
</div>
174160
);
175161
}
176162

177163
export function CliproxyPage() {
178-
const [dialogOpen, setDialogOpen] = useState(false);
179164
const [wizardOpen, setWizardOpen] = useState(false);
165+
const [addAccountProvider, setAddAccountProvider] = useState<{
166+
provider: string;
167+
displayName: string;
168+
} | null>(null);
180169
const { data, isLoading } = useCliproxy();
181170
const { data: authData, isLoading: authLoading } = useCliproxyAuth();
182171
const setDefaultMutation = useSetDefaultAccount();
@@ -191,16 +180,10 @@ export function CliproxyPage() {
191180
Manage OAuth-based provider variants and multi-account configurations
192181
</p>
193182
</div>
194-
<div className="flex items-center gap-2">
195-
<Button variant="outline" onClick={() => setWizardOpen(true)}>
196-
<Sparkles className="w-4 h-4 mr-2" />
197-
Quick Setup
198-
</Button>
199-
<Button onClick={() => setDialogOpen(true)}>
200-
<Plus className="w-4 h-4 mr-2" />
201-
Create Variant
202-
</Button>
203-
</div>
183+
<Button onClick={() => setWizardOpen(true)}>
184+
<Sparkles className="w-4 h-4 mr-2" />
185+
Quick Setup
186+
</Button>
204187
</div>
205188

206189
{/* Built-in Profiles with Account Management */}
@@ -226,6 +209,12 @@ export function CliproxyPage() {
226209
status={status}
227210
setDefaultMutation={setDefaultMutation}
228211
removeMutation={removeMutation}
212+
onAddAccount={() =>
213+
setAddAccountProvider({
214+
provider: status.provider,
215+
displayName: status.displayName,
216+
})
217+
}
229218
/>
230219
))}
231220
</div>
@@ -252,8 +241,13 @@ export function CliproxyPage() {
252241
)}
253242
</div>
254243

255-
<CliproxyDialog open={dialogOpen} onClose={() => setDialogOpen(false)} />
256244
<QuickSetupWizard open={wizardOpen} onClose={() => setWizardOpen(false)} />
245+
<AddAccountDialog
246+
open={addAccountProvider !== null}
247+
onClose={() => setAddAccountProvider(null)}
248+
provider={addAccountProvider?.provider || ''}
249+
displayName={addAccountProvider?.displayName || ''}
250+
/>
257251
</div>
258252
);
259253
}

0 commit comments

Comments
 (0)