11"use client" ;
22
3- import { useEffect , useRef } from "react" ;
3+ import { useState } from "react" ;
44import Link from "next/link" ;
55import { Server , Shield , Users , Zap , Eye , Code , Brain } from "lucide-react" ;
66import { StoryEmbed } from "@/components/story-embed" ;
@@ -17,19 +17,19 @@ function GitHubIcon({ className }: { className?: string }) {
1717// --- See it in Action (Gallery) ---
1818
1919const demos = [
20- {
21- id : "knowledge-bases" ,
22- title : "Knowledge Bases" ,
23- description : "Search uploaded documents with vector search, citations, and inline references." ,
24- storyId : "chat-chatview--knowledge-bases" ,
25- } ,
2620 {
2721 id : "chat" ,
2822 title : "Multi-Model Chat" ,
2923 description :
3024 "Compare responses from multiple models side-by-side with advanced multi-model modes." ,
3125 storyId : "chat-chatview--multi-model-conversation" ,
3226 } ,
27+ {
28+ id : "knowledge-bases" ,
29+ title : "Knowledge Bases" ,
30+ description : "Search uploaded documents with vector search, citations, and inline references." ,
31+ storyId : "chat-chatview--knowledge-bases" ,
32+ } ,
3333 {
3434 id : "execute-code" ,
3535 title : "Execute Code" ,
@@ -39,7 +39,7 @@ const demos = [
3939 {
4040 id : "studio" ,
4141 title : "Studio" ,
42- description : "Generate images across providers simultaneously with cost tracking." ,
42+ description : "Generate media across providers simultaneously with cost tracking." ,
4343 storyId : "pages-studiopage--images" ,
4444 } ,
4545 {
@@ -48,38 +48,68 @@ const demos = [
4848 description : "Track costs per user, team, and project with microcent precision." ,
4949 storyId : "components-usagedashboard--organization" ,
5050 } ,
51+ {
52+ id : "provider-health" ,
53+ title : "Provider Health" ,
54+ description : "Monitor provider status, latency, and circuit breakers in real time." ,
55+ storyId : "admin-providerhealthpage--all-healthy" ,
56+ } ,
57+ {
58+ id : "rbac-policies" ,
59+ title : "RBAC Policies" ,
60+ description : "Define fine-grained access control with CEL-based policies per organization." ,
61+ storyId : "admin-orgrbacpoliciespage--with-policies" ,
62+ } ,
63+ {
64+ id : "multi-tenancy" ,
65+ title : "Multi-Tenancy" ,
66+ description : "Manage organizations with teams, projects, members, and scoped resources." ,
67+ storyId : "admin-organizationdetailpage--default" ,
68+ } ,
5169] ;
5270
5371function DemoGallery ( ) {
54- const chatRef = useRef < HTMLDivElement > ( null ) ;
55-
56- useEffect ( ( ) => {
57- const el = chatRef . current ;
58- const container = el ?. parentElement ;
59- if ( el && container ) {
60- const scrollLeft =
61- el . offsetLeft - container . offsetLeft - ( container . clientWidth - el . offsetWidth ) / 2 ;
62- container . scrollLeft = scrollLeft ;
63- }
64- } , [ ] ) ;
72+ const [ active , setActive ] = useState ( "chat" ) ;
6573
6674 return (
67- < div className = "flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4" >
68- < div className = "w-[15%] shrink-0" aria-hidden = "true" />
69- { demos . map ( ( demo ) => (
70- < div
71- key = { demo . id }
72- ref = { demo . id === "chat" ? chatRef : undefined }
73- className = "w-[70%] shrink-0 snap-center"
74- >
75- < h3 className = "mb-1 text-lg font-semibold" > { demo . title } </ h3 >
76- < p className = "mb-3 text-sm text-fd-muted-foreground" > { demo . description } </ p >
77- < div className = "overflow-hidden rounded-xl border border-fd-border shadow-lg" >
78- < StoryEmbed storyId = { demo . storyId } height = { 850 } />
75+ < div className = "mx-auto max-w-screen-2xl px-4" >
76+ < div
77+ className = "mx-auto mb-6 flex max-w-6xl flex-wrap justify-center gap-2"
78+ role = "tablist"
79+ aria-label = "Demo gallery"
80+ >
81+ { demos . map ( ( demo ) => (
82+ < button
83+ key = { demo . id }
84+ role = "tab"
85+ aria-selected = { active === demo . id }
86+ aria-controls = { `demo-panel-${ demo . id } ` }
87+ onMouseEnter = { ( ) => setActive ( demo . id ) }
88+ onClick = { ( ) => setActive ( demo . id ) }
89+ className = { `shrink-0 rounded-lg border px-4 py-3 text-left transition-colors ${
90+ active === demo . id
91+ ? "border-fd-primary bg-fd-primary/10 text-fd-foreground"
92+ : "border-fd-border bg-fd-card text-fd-muted-foreground hover:border-fd-primary/50 hover:text-fd-foreground"
93+ } `}
94+ >
95+ < span className = "block text-sm font-semibold" > { demo . title } </ span >
96+ < span className = "mt-0.5 block max-w-48 text-xs" > { demo . description } </ span >
97+ </ button >
98+ ) ) }
99+ </ div >
100+ < div className = "relative overflow-hidden rounded-xl border border-fd-border shadow-lg" >
101+ { demos . map ( ( demo ) => (
102+ < div
103+ key = { demo . id }
104+ id = { `demo-panel-${ demo . id } ` }
105+ role = "tabpanel"
106+ aria-label = { demo . title }
107+ className = { active === demo . id ? "" : "invisible absolute inset-0" }
108+ >
109+ < StoryEmbed storyId = { demo . storyId } height = { 950 } />
79110 </ div >
80- </ div >
81- ) ) }
82- < div className = "w-[15%] shrink-0" aria-hidden = "true" />
111+ ) ) }
112+ </ div >
83113 </ div >
84114 ) ;
85115}
0 commit comments