- 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.
64 lines
1.9 KiB
TypeScript
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 }]
|
|
}
|