fix(accounts-ui): move action center content into left column

This commit is contained in:
Tam Nhu Tran
2026-02-26 21:11:35 +07:00
parent 1708128a62
commit cfdad81bee
+162 -228
View File
@@ -5,15 +5,7 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import { AlertTriangle, ArrowRight, ChevronDown, Plus, Users, Zap } from 'lucide-react';
AlertTriangle,
ArrowRight,
ChevronDown,
ChevronLeft,
Plus,
Users,
Zap,
} from 'lucide-react';
import { AccountsTable } from '@/components/account/accounts-table'; import { AccountsTable } from '@/components/account/accounts-table';
import { CreateAuthProfileDialog } from '@/components/account/create-auth-profile-dialog'; import { CreateAuthProfileDialog } from '@/components/account/create-auth-profile-dialog';
import { HistorySyncLearningMap } from '@/components/account/history-sync-learning-map'; import { HistorySyncLearningMap } from '@/components/account/history-sync-learning-map';
@@ -31,7 +23,6 @@ export function AccountsPage() {
const { data, isLoading } = useAccounts(); const { data, isLoading } = useAccounts();
const confirmLegacyMutation = useConfirmLegacyAccountPolicies(); const confirmLegacyMutation = useConfirmLegacyAccountPolicies();
const [createDialogOpen, setCreateDialogOpen] = useState(false); const [createDialogOpen, setCreateDialogOpen] = useState(false);
const [showGuideRail, setShowGuideRail] = useState(true);
const [guideOpen, setGuideOpen] = useState(false); const [guideOpen, setGuideOpen] = useState(false);
const authAccounts = data?.accounts || []; const authAccounts = data?.accounts || [];
@@ -63,34 +54,69 @@ export function AccountsPage() {
return ( return (
<> <>
<div className="h-[calc(100vh-100px)] hidden lg:flex"> <div className="h-[calc(100vh-100px)] hidden lg:flex">
{/* Left rail */} {/* Left action column */}
<div className="w-80 border-r flex flex-col bg-muted/30 shrink-0"> <div className="w-80 border-r flex flex-col bg-muted/20 shrink-0">
<div className="p-4 border-b bg-background space-y-3"> <div className="p-4 border-b bg-background space-y-2">
<div> <div className="flex items-center gap-2">
<div className="flex items-center gap-2"> <Users className="h-5 w-5 text-primary" />
<Users className="h-5 w-5 text-primary" /> <h1 className="font-semibold">Accounts</h1>
<h1 className="font-semibold">Accounts</h1> <Badge variant="outline" className="ml-auto">
</div> Action Center
<p className="mt-1 text-xs text-muted-foreground"> </Badge>
Dedicated
<code className="mx-1 rounded bg-muted px-1 py-0.5">ccs auth</code>
continuity controls.
</p>
</div> </div>
<p className="text-xs text-muted-foreground">
<div className="grid grid-cols-1 gap-2"> Manage
<Button onClick={() => setCreateDialogOpen(true)} size="sm" className="justify-start"> <code className="mx-1 rounded bg-muted px-1 py-0.5">ccs auth</code>
<Plus className="w-4 h-4 mr-2" /> accounts and pool onboarding from one panel.
Create Account
</Button>
</div>
<p className="text-[11px] text-muted-foreground">
Pool auth actions live in Action Center to avoid duplicate controls.
</p> </p>
</div> </div>
<ScrollArea className="flex-1"> <ScrollArea className="flex-1">
<div className="p-3 space-y-3"> <div className="p-4 space-y-3">
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Immediate Actions</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<Button
size="sm"
className="w-full justify-start"
onClick={() => setCreateDialogOpen(true)}
>
<Plus className="w-4 h-4 mr-2" />
Create Account
</Button>
<Button
size="sm"
className="w-full justify-start"
onClick={handleOpenClaudePoolAuth}
>
<Zap className="w-4 h-4 mr-2" />
Authenticate Claude in Pool
</Button>
<Button
variant="outline"
size="sm"
className="w-full justify-start"
onClick={handleOpenClaudePool}
>
Open Claude Pool Settings
<ArrowRight className="w-4 h-4 ml-auto" />
</Button>
<Button
variant="outline"
size="sm"
className="w-full justify-start"
onClick={handleConfirmLegacy}
disabled={confirmLegacyMutation.isPending || legacyTargetCount === 0}
>
{confirmLegacyMutation.isPending
? 'Confirming Legacy Policies...'
: `Confirm Legacy Policies (${legacyTargetCount})`}
</Button>
</CardContent>
</Card>
{hasLegacyFollowUp && ( {hasLegacyFollowUp && (
<section className="space-y-2"> <section className="space-y-2">
<p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground"> <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
@@ -116,218 +142,126 @@ export function AccountsPage() {
)} )}
</div> </div>
</div> </div>
<Button
variant="secondary"
size="sm"
className="w-full justify-start"
onClick={handleConfirmLegacy}
disabled={confirmLegacyMutation.isPending || legacyTargetCount === 0}
>
{confirmLegacyMutation.isPending
? 'Confirming Legacy Accounts...'
: `Confirm Current Policy for ${legacyTargetCount} Legacy Account${legacyTargetCount > 1 ? 's' : ''}`}
</Button>
</div> </div>
</section> </section>
)} )}
{!hasLegacyFollowUp && ( {!hasLegacyFollowUp && (
<div className="rounded-md border bg-background px-3 py-2 text-xs text-muted-foreground"> <div className="rounded-md border bg-background px-3 py-2 text-xs text-muted-foreground">
No legacy follow-up pending. Manage pool auth from Action Center. No legacy follow-up pending.
</div> </div>
)} )}
<Collapsible open={guideOpen} onOpenChange={setGuideOpen}>
<Card>
<CardHeader className="pb-2">
<CollapsibleTrigger asChild>
<Button variant="ghost" className="h-auto w-full justify-between px-0 py-0">
<div className="text-left">
<CardTitle className="text-sm">Continuity Guide</CardTitle>
<CardDescription className="mt-1">
Expand only when needed.
</CardDescription>
</div>
<ChevronDown
className={cn('h-4 w-4 transition-transform', guideOpen && 'rotate-180')}
/>
</Button>
</CollapsibleTrigger>
</CardHeader>
<CollapsibleContent>
<CardContent className="space-y-3 text-xs text-muted-foreground">
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Shared Standard</p>
<p className="mt-1">
Project workspace sync only. Best default for most teams.
</p>
</div>
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Shared Deeper</p>
<p className="mt-1">
Adds <code>session-env</code>, <code>file-history</code>,{' '}
<code>shell-snapshots</code>, <code>todos</code>.
</p>
</div>
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Isolated</p>
<p className="mt-1">No link. Best for strict separation.</p>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Quick Commands</CardTitle>
<CardDescription>Copy and run in terminal.</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="rounded-md border bg-background px-2 py-2 font-mono text-[11px] flex items-start gap-2">
<span className="flex-1 break-all">
ccs auth create work --context-group sprint-a --deeper-continuity
</span>
<CopyButton
value="ccs auth create work --context-group sprint-a --deeper-continuity"
size="icon"
/>
</div>
<div className="rounded-md border bg-background px-2 py-2 font-mono text-[11px] flex items-start gap-2">
<span className="flex-1 break-all">ccs cliproxy auth claude</span>
<CopyButton value="ccs cliproxy auth claude" size="icon" />
</div>
</CardContent>
</Card>
</div> </div>
</ScrollArea> </ScrollArea>
</div> </div>
{/* Main workspace */} {/* Main workspace */}
<div className="flex-1 min-w-0 flex"> <div className="flex-1 min-w-0 flex flex-col bg-background">
{/* Table column */} <div className="px-5 py-4 border-b bg-background">
<div className="flex-1 min-w-0 flex flex-col border-r bg-background"> <div className="flex items-center gap-2">
<div className="px-5 py-4 border-b bg-background"> <Badge variant="outline">ccs auth Workspace</Badge>
<div className="flex items-center gap-2"> <Badge variant="secondary">History Sync Controls</Badge>
<Badge variant="outline">ccs auth Workspace</Badge>
<Badge variant="secondary">History Sync Controls</Badge>
<Button
variant="outline"
size="sm"
className="ml-auto"
onClick={() => setShowGuideRail((prev) => !prev)}
>
{showGuideRail ? 'Hide Action Center' : 'Show Action Center'}
</Button>
</div>
<h2 className="mt-2 text-xl font-semibold tracking-tight">Auth Accounts</h2>
<p className="mt-1 text-sm text-muted-foreground">
This table is intentionally scoped to
<code className="mx-1 rounded bg-muted px-1 py-0.5">ccs auth</code>
accounts. Use
<code className="mx-1 rounded bg-muted px-1 py-0.5">Sync</code>
for mode/group/depth changes.
</p>
</div>
<div className="flex-1 min-h-0 p-5 space-y-4 overflow-y-auto">
<HistorySyncLearningMap
isolatedCount={isolatedCount}
sharedStandardCount={sharedStandardCount}
deeperSharedCount={deeperSharedCount}
sharedGroups={sharedGroups}
legacyTargetCount={legacyTargetCount}
cliproxyCount={cliproxyCount}
/>
<Card className="flex flex-col">
<CardHeader className="pb-3">
<CardTitle className="text-lg">Account Matrix</CardTitle>
<CardDescription>
Shared total: {sharedCount}. Actions include Sync settings and legacy
confirmation.
</CardDescription>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="text-muted-foreground">Loading accounts...</div>
) : (
<AccountsTable data={authAccounts} defaultAccount={data?.default ?? null} />
)}
</CardContent>
</Card>
</div> </div>
<h2 className="mt-2 text-xl font-semibold tracking-tight">Auth Accounts</h2>
<p className="mt-1 text-sm text-muted-foreground">
This table is intentionally scoped to
<code className="mx-1 rounded bg-muted px-1 py-0.5">ccs auth</code>
accounts. Use
<code className="mx-1 rounded bg-muted px-1 py-0.5">Sync</code>
for mode/group/depth changes.
</p>
</div> </div>
{/* Right action center */} <div className="flex-1 min-h-0 p-5 space-y-4 overflow-y-auto">
{showGuideRail ? ( <HistorySyncLearningMap
<div className="w-80 shrink-0 flex flex-col bg-muted/20"> isolatedCount={isolatedCount}
<div className="p-4 border-b bg-background flex items-start justify-between gap-3"> sharedStandardCount={sharedStandardCount}
<div> deeperSharedCount={deeperSharedCount}
<h3 className="font-semibold">Action Center</h3> sharedGroups={sharedGroups}
<p className="mt-1 text-xs text-muted-foreground"> legacyTargetCount={legacyTargetCount}
High-value actions for pool auth and legacy cleanup. cliproxyCount={cliproxyCount}
</p> />
</div>
<Button variant="ghost" size="icon" onClick={() => setShowGuideRail(false)}>
<ChevronLeft className="h-4 w-4" />
</Button>
</div>
<ScrollArea className="flex-1"> <Card className="flex flex-col">
<div className="p-4 space-y-3"> <CardHeader className="pb-3">
<Card> <CardTitle className="text-lg">Account Matrix</CardTitle>
<CardHeader className="pb-2"> <CardDescription>
<CardTitle className="text-sm">Immediate Actions</CardTitle> Shared total: {sharedCount}. Actions include Sync settings and legacy
</CardHeader> confirmation.
<CardContent className="space-y-2"> </CardDescription>
<Button </CardHeader>
size="sm" <CardContent>
className="w-full justify-start" {isLoading ? (
onClick={handleOpenClaudePoolAuth} <div className="text-muted-foreground">Loading accounts...</div>
> ) : (
<Zap className="w-4 h-4 mr-2" /> <AccountsTable data={authAccounts} defaultAccount={data?.default ?? null} />
Authenticate Claude in Pool )}
</Button> </CardContent>
<Button </Card>
variant="outline" </div>
size="sm"
className="w-full justify-start"
onClick={handleOpenClaudePool}
>
Open Claude Pool Settings
<ArrowRight className="w-4 h-4 ml-auto" />
</Button>
<Button
variant="outline"
size="sm"
className="w-full justify-start"
onClick={handleConfirmLegacy}
disabled={confirmLegacyMutation.isPending || legacyTargetCount === 0}
>
{confirmLegacyMutation.isPending
? 'Confirming Legacy Policies...'
: `Confirm Legacy Policies (${legacyTargetCount})`}
</Button>
</CardContent>
</Card>
<Collapsible open={guideOpen} onOpenChange={setGuideOpen}>
<Card>
<CardHeader className="pb-2">
<CollapsibleTrigger asChild>
<Button
variant="ghost"
className="h-auto w-full justify-between px-0 py-0"
>
<div className="text-left">
<CardTitle className="text-sm">Continuity Guide</CardTitle>
<CardDescription className="mt-1">
Expand only when needed.
</CardDescription>
</div>
<ChevronDown
className={cn(
'h-4 w-4 transition-transform',
guideOpen && 'rotate-180'
)}
/>
</Button>
</CollapsibleTrigger>
</CardHeader>
<CollapsibleContent>
<CardContent className="space-y-3 text-xs text-muted-foreground">
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Shared Standard</p>
<p className="mt-1">
Project workspace sync only. Best default for most teams.
</p>
</div>
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Shared Deeper</p>
<p className="mt-1">
Adds <code>session-env</code>, <code>file-history</code>,{' '}
<code>shell-snapshots</code>, <code>todos</code>.
</p>
</div>
<div className="rounded-md border p-2.5">
<p className="font-semibold text-foreground">Isolated</p>
<p className="mt-1">No link. Best for strict separation.</p>
</div>
</CardContent>
</CollapsibleContent>
</Card>
</Collapsible>
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm">Quick Commands</CardTitle>
<CardDescription>Copy and run in terminal.</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="rounded-md border bg-background px-2 py-2 font-mono text-[11px] flex items-start gap-2">
<span className="flex-1 break-all">
ccs auth create work --context-group sprint-a --deeper-continuity
</span>
<CopyButton
value="ccs auth create work --context-group sprint-a --deeper-continuity"
size="icon"
/>
</div>
<div className="rounded-md border bg-background px-2 py-2 font-mono text-[11px] flex items-start gap-2">
<span className="flex-1 break-all">ccs cliproxy auth claude</span>
<CopyButton value="ccs cliproxy auth claude" size="icon" />
</div>
</CardContent>
</Card>
</div>
</ScrollArea>
</div>
) : (
<div className="w-14 shrink-0 border-l bg-muted/20 flex items-start justify-center pt-3">
<Button variant="ghost" size="icon" onClick={() => setShowGuideRail(true)}>
<ChevronLeft className="h-4 w-4 rotate-180" />
</Button>
</div>
)}
</div> </div>
</div> </div>