Files
group_xinghuo_jinrong/web/src/utils/mergeProductNavRows.ts
T
zhanghongyu_0626 ad930c26b1 feat(frontend): Implement initial P0 scaffolding and enhance dashboard features
- Established the P0 scaffolding for the frontend, including login, layout, and routing for four roles.
- Integrated the Customer Wealth Dashboard, Advisor Clients Dashboard, Analyst Market Dashboard, and Risk Alerts Dashboard.
- Updated the API client to support fetching customer and product data, enhancing the overall functionality of the dashboard.
- Added error handling components to improve user experience during data fetching.
- Enhanced charting capabilities using Ant Design Charts for better data visualization.

This update lays the groundwork for further development of the frontend application, ensuring a robust structure for future features and integrations.
2026-09-09 15:50:33 +08:00

64 lines
1.9 KiB
TypeScript

import type { ChartDatum, ProductNav, ProductRow } from './dashboardTypes'
export function mergeProductNavRows(
products: Omit<ProductRow, 'nav' | 'daily_chg_pct' | 'nav_date'>[],
navMap: Record<string, ProductNav | undefined>,
): ProductRow[] {
return products.map((p) => {
const nav = navMap[p.product_id]
if (!nav) return { ...p, daily_chg_pct: null }
return {
...p,
nav: nav.nav,
daily_chg_pct: nav.daily_chg_pct,
nav_date: nav.nav_date,
}
})
}
export function countMarketMoves(rows: ProductRow[]): { up: number; down: number; flat: number } {
let up = 0
let down = 0
let flat = 0
for (const row of rows) {
const v = row.daily_chg_pct
if (v == null) continue
if (v > 0) up += 1
else if (v < 0) down += 1
else flat += 1
}
return { up, down, flat }
}
export function avgChgByType(rows: ProductRow[]): ChartDatum[] {
const buckets = new Map<string, { sum: number; count: number }>()
for (const row of rows) {
if (row.daily_chg_pct == null) continue
const key = row.product_type
const cur = buckets.get(key) ?? { sum: 0, count: 0 }
cur.sum += row.daily_chg_pct
cur.count += 1
buckets.set(key, cur)
}
return [...buckets.entries()].map(([label, { sum, count }]) => ({
label,
value: count ? sum / count : 0,
}))
}
export function typeDistribution(rows: ProductRow[]): ChartDatum[] {
const counts = new Map<string, number>()
for (const row of rows) {
counts.set(row.product_type, (counts.get(row.product_type) ?? 0) + 1)
}
return [...counts.entries()].map(([label, value]) => ({ label, value }))
}
export function topNChartData(items: ChartDatum[], n = 5): ChartDatum[] {
const sorted = [...items].sort((a, b) => b.value - a.value)
if (sorted.length <= n) return sorted
const top = sorted.slice(0, n)
const rest = sorted.slice(n).reduce((s, x) => s + x.value, 0)
return [...top, { label: '其他', value: rest }]
}