feat(dashboard): add traffic flow sankey chart (#5465)

* feat(dashboard): add traffic flow sankey chart

Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests.

* feat: build flow chart from quota data

---------

Co-authored-by: CaIon <i@caion.me>
This commit is contained in:
Quaternijkon
2026-06-20 18:57:47 +08:00
committed by GitHub
co-authored by CaIon
parent f9e508bdae
commit a68041f7d6
29 changed files with 2659 additions and 77 deletions
+29 -2
View File
@@ -77,6 +77,12 @@ const LazyUserCharts = lazy(() =>
}))
)
const LazyFlowCharts = lazy(() =>
import('./components/flow/flow-charts').then((m) => ({
default: m.FlowCharts,
}))
)
function LogStatCardsFallback() {
return (
<div className='overflow-hidden rounded-lg border'>
@@ -137,6 +143,9 @@ const SECTION_META: Record<DashboardSectionId, { titleKey: string }> = {
models: {
titleKey: 'Model Call Analytics',
},
flow: {
titleKey: 'Flow',
},
users: {
titleKey: 'User Analytics',
},
@@ -217,6 +226,17 @@ export function Dashboard() {
/>
</>
) : null
const flowActions =
activeSection === 'flow' ? (
<ModelsFilter
preferences={chartPreferences}
onFilterChange={handleFilterChange}
onReset={handleResetFilters}
titleKey='Flow Filters'
descriptionKey='Filter the traffic flow view by time range and user.'
/>
) : null
const sectionActions = modelActions ?? flowActions
return (
<SectionPageLayout>
@@ -238,9 +258,9 @@ export function Dashboard() {
) : (
<div />
)}
{modelActions != null && (
{sectionActions != null && (
<div className='flex shrink-0 flex-wrap items-center gap-1.5 sm:gap-2'>
{modelActions}
{sectionActions}
</div>
)}
</div>
@@ -298,6 +318,13 @@ export function Dashboard() {
</Suspense>
</FadeIn>
)}
{activeSection === 'flow' && (
<FadeIn>
<Suspense fallback={<ModelChartsFallback />}>
<LazyFlowCharts filters={modelFilters} />
</Suspense>
</FadeIn>
)}
</div>
</SectionPageLayout.Content>
</SectionPageLayout>