mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +00:00
feat(accounts-ui): add history sync learning visualization
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import {
|
||||
ArrowRight,
|
||||
ArrowRightLeft,
|
||||
Layers3,
|
||||
Link2,
|
||||
Unlink,
|
||||
Waves,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface HistorySyncLearningMapProps {
|
||||
isolatedCount: number;
|
||||
sharedStandardCount: number;
|
||||
deeperSharedCount: number;
|
||||
sharedGroups: string[];
|
||||
legacyTargetCount: number;
|
||||
}
|
||||
|
||||
type StageTone = 'isolated' | 'shared' | 'deeper';
|
||||
|
||||
function StageCard({
|
||||
title,
|
||||
count,
|
||||
icon: Icon,
|
||||
tone,
|
||||
description,
|
||||
}: {
|
||||
title: string;
|
||||
count: number;
|
||||
icon: LucideIcon;
|
||||
tone: StageTone;
|
||||
description: string;
|
||||
}) {
|
||||
const toneClasses: Record<StageTone, { card: string; icon: string; count: string }> = {
|
||||
isolated: {
|
||||
card: 'border-blue-300/60 bg-blue-50/40 dark:border-blue-900/40 dark:bg-blue-900/10',
|
||||
icon: 'text-blue-700 dark:text-blue-400',
|
||||
count: 'text-blue-700 dark:text-blue-400',
|
||||
},
|
||||
shared: {
|
||||
card: 'border-emerald-300/60 bg-emerald-50/40 dark:border-emerald-900/40 dark:bg-emerald-900/10',
|
||||
icon: 'text-emerald-700 dark:text-emerald-400',
|
||||
count: 'text-emerald-700 dark:text-emerald-400',
|
||||
},
|
||||
deeper: {
|
||||
card: 'border-indigo-300/60 bg-indigo-50/40 dark:border-indigo-900/40 dark:bg-indigo-900/10',
|
||||
icon: 'text-indigo-700 dark:text-indigo-400',
|
||||
count: 'text-indigo-700 dark:text-indigo-400',
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('rounded-md border p-3', toneClasses[tone].card)}>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-sm font-semibold">{title}</p>
|
||||
<Icon className={cn('h-4 w-4', toneClasses[tone].icon)} />
|
||||
</div>
|
||||
<p className={cn('mt-1 text-2xl font-mono font-semibold', toneClasses[tone].count)}>
|
||||
{count}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function HistorySyncLearningMap({
|
||||
isolatedCount,
|
||||
sharedStandardCount,
|
||||
deeperSharedCount,
|
||||
sharedGroups,
|
||||
legacyTargetCount,
|
||||
}: HistorySyncLearningMapProps) {
|
||||
const groupsToShow = sharedGroups.length > 0 ? sharedGroups : ['default'];
|
||||
|
||||
return (
|
||||
<Card className="border-dashed">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<CardTitle className="text-base">How History Sync Works</CardTitle>
|
||||
<Badge variant="outline">Learning Map</Badge>
|
||||
</div>
|
||||
<CardDescription>
|
||||
Accounts can move between modes at any time. Link/unlink is instant; Edit gives full
|
||||
control of group and continuity depth.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid gap-3 lg:grid-cols-[1fr_auto_1fr_auto_1fr] lg:items-center">
|
||||
<StageCard
|
||||
title="Isolated"
|
||||
count={isolatedCount}
|
||||
icon={Unlink}
|
||||
tone="isolated"
|
||||
description="No shared history with other accounts."
|
||||
/>
|
||||
<div className="hidden lg:flex justify-center text-muted-foreground">
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</div>
|
||||
<StageCard
|
||||
title="Shared (Standard)"
|
||||
count={sharedStandardCount}
|
||||
icon={Link2}
|
||||
tone="shared"
|
||||
description="Shared project context only."
|
||||
/>
|
||||
<div className="hidden lg:flex justify-center text-muted-foreground">
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</div>
|
||||
<StageCard
|
||||
title="Shared (Deeper)"
|
||||
count={deeperSharedCount}
|
||||
icon={Waves}
|
||||
tone="deeper"
|
||||
description="Adds continuity data: session-env/file-history/todos/shell-snapshots."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 lg:grid-cols-2">
|
||||
<div className="rounded-md border bg-muted/20 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowRightLeft className="h-4 w-4 text-muted-foreground" />
|
||||
<p className="text-sm font-semibold">Mode Switch Actions</p>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
<Badge variant="secondary">Link: Isolated -> Shared</Badge>
|
||||
<Badge variant="secondary">Unlink: Shared -> Isolated</Badge>
|
||||
<Badge variant="secondary">Edit: Group + Deeper</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border bg-muted/20 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Layers3 className="h-4 w-4 text-muted-foreground" />
|
||||
<p className="text-sm font-semibold">History Sync Group</p>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Accounts in the same group share the same project context bucket. Group names are
|
||||
user-defined lanes, with <code>default</code> as fallback.
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
{groupsToShow.map((group) => (
|
||||
<Badge key={group} variant="outline" className="font-mono text-[11px]">
|
||||
{group}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{legacyTargetCount > 0 && (
|
||||
<div className="rounded-md border border-amber-500/50 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-300">
|
||||
{legacyTargetCount} legacy account
|
||||
{legacyTargetCount > 1 ? 's still need' : ' still needs'} explicit confirmation. Use{' '}
|
||||
<strong>Confirm Legacy Policies</strong> in Action Center.
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
+19
-18
@@ -19,6 +19,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { AccountsTable } from '@/components/account/accounts-table';
|
||||
import { CreateAuthProfileDialog } from '@/components/account/create-auth-profile-dialog';
|
||||
import { HistorySyncLearningMap } from '@/components/account/history-sync-learning-map';
|
||||
import { CopyButton } from '@/components/ui/copy-button';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -115,6 +116,13 @@ export function AccountsPage() {
|
||||
const sharedStandardCount = data?.sharedStandardCount || 0;
|
||||
const deeperSharedCount = data?.deeperSharedCount || 0;
|
||||
const isolatedCount = data?.isolatedCount || 0;
|
||||
const sharedGroups = Array.from(
|
||||
new Set(
|
||||
authAccounts
|
||||
.filter((account) => account.context_mode === 'shared')
|
||||
.map((account) => account.context_group || 'default')
|
||||
)
|
||||
).sort((a, b) => a.localeCompare(b));
|
||||
|
||||
const legacyTargets = authAccounts.filter(
|
||||
(account) => account.context_inferred || account.continuity_inferred
|
||||
@@ -149,25 +157,10 @@ export function AccountsPage() {
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create Account
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="justify-start"
|
||||
onClick={handleOpenClaudePool}
|
||||
>
|
||||
Open CLIProxy Claude Pool
|
||||
<ArrowRight className="w-4 h-4 ml-auto" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="justify-start"
|
||||
onClick={handleOpenClaudePoolAuth}
|
||||
>
|
||||
Authenticate Claude in Pool
|
||||
<Zap className="w-4 h-4 ml-auto" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Pool auth actions live in Action Center to avoid duplicate controls.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1">
|
||||
@@ -302,6 +295,14 @@ export function AccountsPage() {
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<HistorySyncLearningMap
|
||||
isolatedCount={isolatedCount}
|
||||
sharedStandardCount={sharedStandardCount}
|
||||
deeperSharedCount={deeperSharedCount}
|
||||
sharedGroups={sharedGroups}
|
||||
legacyTargetCount={legacyTargetCount}
|
||||
/>
|
||||
|
||||
<Card className="flex flex-col">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg">Account Matrix</CardTitle>
|
||||
|
||||
Reference in New Issue
Block a user