mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-21 00:24:31 +00:00
feat(ui): add updates center with support matrix
- add data-driven catalog for support notices and CLI compatibility - introduce Updates Center page with announcements, filtering, and search - render reusable support cards and status badges for future expansions
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowUpRight } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
SUPPORT_SCOPE_LABELS,
|
||||
type CliSupportEntry,
|
||||
type SupportScope,
|
||||
} from '@/lib/support-updates-catalog';
|
||||
import { SupportStatusBadge } from './support-status-badge';
|
||||
|
||||
const PILLAR_LABELS: { key: keyof CliSupportEntry['pillars']; label: string }[] = [
|
||||
{ key: 'baseUrl', label: 'Base URL' },
|
||||
{ key: 'auth', label: 'Auth' },
|
||||
{ key: 'model', label: 'Model' },
|
||||
];
|
||||
|
||||
const SCOPE_STYLES: Record<SupportScope, string> = {
|
||||
target:
|
||||
'border-violet-200 bg-violet-50 text-violet-700 dark:border-violet-900/50 dark:bg-violet-900/20 dark:text-violet-300',
|
||||
cliproxy:
|
||||
'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/50 dark:bg-sky-900/20 dark:text-sky-300',
|
||||
'api-profiles':
|
||||
'border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/50 dark:bg-emerald-900/20 dark:text-emerald-300',
|
||||
websearch:
|
||||
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/50 dark:bg-amber-900/20 dark:text-amber-300',
|
||||
};
|
||||
|
||||
export function SupportEntryCard({ entry }: { entry: CliSupportEntry }) {
|
||||
return (
|
||||
<Card className="h-full gap-4">
|
||||
<CardHeader className="gap-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="space-y-2">
|
||||
<CardTitle className="text-base leading-tight">{entry.name}</CardTitle>
|
||||
<CardDescription>{entry.summary}</CardDescription>
|
||||
</div>
|
||||
<SupportStatusBadge status={entry.status} />
|
||||
</div>
|
||||
|
||||
<Badge variant="outline" className={SCOPE_STYLES[entry.scope]}>
|
||||
{SUPPORT_SCOPE_LABELS[entry.scope]}
|
||||
</Badge>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid gap-2 sm:grid-cols-3">
|
||||
{PILLAR_LABELS.map((pillar) => (
|
||||
<div key={pillar.key} className="rounded-md border bg-muted/30 p-2">
|
||||
<p className="text-[10px] uppercase tracking-wide text-muted-foreground">
|
||||
{pillar.label}
|
||||
</p>
|
||||
<p className="text-xs font-medium leading-relaxed">{entry.pillars[pillar.key]}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||
Dashboard
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{entry.routes.map((route) => (
|
||||
<Link
|
||||
key={`${entry.id}-${route.path}`}
|
||||
to={route.path}
|
||||
className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs hover:bg-muted"
|
||||
>
|
||||
{route.label}
|
||||
<ArrowUpRight className="h-3 w-3" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||
CLI Usage
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{entry.commands.slice(0, 2).map((command) => (
|
||||
<code
|
||||
key={`${entry.id}-${command}`}
|
||||
className="block rounded bg-muted px-2 py-1 text-[11px] leading-relaxed"
|
||||
>
|
||||
{command}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{entry.notes && <p className="text-xs text-muted-foreground">{entry.notes}</p>}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { SupportStatus } from '@/lib/support-updates-catalog';
|
||||
|
||||
const STATUS_LABELS: Record<SupportStatus, string> = {
|
||||
new: 'New',
|
||||
stable: 'Stable',
|
||||
planned: 'Planned',
|
||||
};
|
||||
|
||||
const STATUS_STYLES: Record<SupportStatus, string> = {
|
||||
new: 'border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-900/50 dark:bg-blue-900/20 dark:text-blue-300',
|
||||
stable:
|
||||
'border-green-200 bg-green-50 text-green-700 dark:border-green-900/50 dark:bg-green-900/20 dark:text-green-300',
|
||||
planned:
|
||||
'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/50 dark:bg-amber-900/20 dark:text-amber-300',
|
||||
};
|
||||
|
||||
export function SupportStatusBadge({
|
||||
status,
|
||||
className,
|
||||
}: {
|
||||
status: SupportStatus;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<Badge variant="outline" className={cn('font-medium', STATUS_STYLES[status], className)}>
|
||||
{STATUS_LABELS[status]}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user