diff --git a/ui/src/pages/updates.tsx b/ui/src/pages/updates.tsx index 24f7cd2b..06d2597e 100644 --- a/ui/src/pages/updates.tsx +++ b/ui/src/pages/updates.tsx @@ -1,10 +1,11 @@ import { useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; -import { BellRing, Filter, Megaphone, Search } from 'lucide-react'; +import { BellRing, ChevronRight, Filter, Megaphone, Search } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; -import { Alert, AlertDescription } from '@/components/ui/alert'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { cn } from '@/lib/utils'; import { SupportEntryCard } from '@/components/updates/support-entry-card'; import { SupportStatusBadge } from '@/components/updates/support-status-badge'; import { @@ -12,6 +13,7 @@ import { SUPPORT_NOTICES, SUPPORT_SCOPE_LABELS, formatCatalogDate, + type SupportNotice, type SupportScope, } from '@/lib/support-updates-catalog'; @@ -25,9 +27,77 @@ const SCOPE_FILTERS: { id: ScopeFilter; label: string }[] = [ { id: 'websearch', label: SUPPORT_SCOPE_LABELS.websearch }, ]; +const CORE_CONTRACT = [ + { + id: 'base-url', + title: 'Base URL', + detail: 'Source endpoint is explicit per target/provider/profile.', + }, + { + id: 'auth', + title: 'Auth', + detail: 'OAuth or token ownership is visible in one support matrix.', + }, + { + id: 'model', + title: 'Model', + detail: 'Default model behavior stays configurable and documented.', + }, +] as const; + +function NoticeListItem({ + notice, + isSelected, + onSelect, +}: { + notice: SupportNotice; + isSelected: boolean; + onSelect: () => void; +}) { + return ( + + ); +} + export function UpdatesPage() { const [scope, setScope] = useState('all'); const [query, setQuery] = useState(''); + const [selectedNoticeId, setSelectedNoticeId] = useState( + SUPPORT_NOTICES[0]?.id ?? null + ); + + const selectedNotice = useMemo( + () => SUPPORT_NOTICES.find((notice) => notice.id === selectedNoticeId) ?? SUPPORT_NOTICES[0], + [selectedNoticeId] + ); const filteredEntries = useMemo(() => { const queryValue = query.trim().toLowerCase(); @@ -55,160 +125,216 @@ export function UpdatesPage() { }); }, [scope, query]); + const scopeStats = useMemo( + () => + SCOPE_FILTERS.filter((filter) => filter.id !== 'all').map((filter) => ({ + id: filter.id, + label: filter.label, + count: CLI_SUPPORT_ENTRIES.filter((entry) => entry.scope === filter.id).length, + })), + [] + ); + return ( -
- - - - - CCS Updates Center - - - Release visibility for runtime support, CLIProxy providers, and integration readiness. - - - - - - - This page is data-driven. Update one catalog file to publish new support notices - across dashboard surfaces. - - - -
- - ccsd glm - - - ccs codex --target droid "your prompt" - - - ccs cliproxy create mycodex --provider codex --target droid - +
+
+
+
+ +

Updates Center

- - - -
-
-

Announcements

- {SUPPORT_NOTICES.length} published -
- -
- {SUPPORT_NOTICES.map((notice) => ( - - -
-
- {notice.title} - {notice.summary} -
- -
-

- {formatCatalogDate(notice.publishedAt)} -

-
- -
    - {notice.highlights.map((highlight) => ( -
  • {highlight}
  • - ))} -
- -
- {notice.routes.map((route) => ( - - {route.label} - - ))} -
-
-
- ))} -
-
- -
-
-
-

Support Matrix

-

- Search by CLI/provider and filter by support surface. -

-
- -
- +

+ Release visibility for target, provider, and support rollouts. +

+
+ setQuery(event.target.value)} - placeholder="Search by command, provider, or note" - className="pl-8" + placeholder="Search support matrix" + className="pl-8 h-9" />
-
- - - Scope: - - {SCOPE_FILTERS.map((filter) => ( - - ))} - {filteredEntries.length} entries -
- - {filteredEntries.length === 0 ? ( - - - No support entries match this filter. - - - ) : ( -
- {filteredEntries.map((entry) => ( - + +
+ {SUPPORT_NOTICES.map((notice) => ( + setSelectedNoticeId(notice.id)} + /> ))}
- )} -
+ - - - Maintainer Notes - - Keep update messaging in one place for future CLI expansions. - - - -

- Edit{' '} - - ui/src/lib/support-updates-catalog.ts - {' '} - to add new notices or support entries. -

-

- Home spotlight and this page consume the same catalog, so announcements stay consistent - without repeated UI edits. -

-
-
+
+
+ + {SUPPORT_NOTICES.length} notice{SUPPORT_NOTICES.length !== 1 ? 's' : ''} + + + {CLI_SUPPORT_ENTRIES.length} support entr + {CLI_SUPPORT_ENTRIES.length !== 1 ? 'ies' : 'y'} + +
+
+
+ +
+ {selectedNotice && ( +
+
+
+

{selectedNotice.title}

+

{selectedNotice.summary}

+
+ +
+ +
+ + Published {formatCatalogDate(selectedNotice.publishedAt)} +
+ +
    + {selectedNotice.highlights.map((highlight) => ( +
  • - {highlight}
  • + ))} +
+ +
+ {selectedNotice.routes.map((route) => ( + + {route.label} + + ))} +
+ +
+ {selectedNotice.commands.map((command) => ( + + {command} + + ))} +
+
+ )} + +
+
+ + + Support Matrix + + Filter by support area. Internal scroll keeps the page frame stable. + + + + +
+
+ + + Scope: + + {SCOPE_FILTERS.map((filter) => ( + + ))} + + {filteredEntries.length} match + +
+ +
+ + {filteredEntries.length === 0 ? ( +
+ No support entries match this filter. +
+ ) : ( +
+ {filteredEntries.map((entry) => ( + + ))} +
+ )} +
+
+
+
+
+ + + + Config Contract + + Every new CLI integration follows the same three configuration pillars. + + + + + +
+
+ {CORE_CONTRACT.map((item) => ( +
+

+ {item.title} +

+

{item.detail}

+
+ ))} +
+ +
+

+ Coverage by Scope +

+
+ {scopeStats.map((stat) => ( +
+ {stat.label} + + {stat.count} + +
+ ))} +
+
+ +
+ Update source of truth:{' '} + + ui/src/lib/support-updates-catalog.ts + +
+
+
+
+
+
+
+
); }