|
@@ -1,5 +1,5 @@
|
|
|
-import { getChartColor } from '@/lib/colors'
|
|
|
|
|
-import { formatQuotaWithCurrency, getCurrencyDisplay } from '@/lib/currency'
|
|
|
|
|
|
|
+import { dataScheme as vchartDefaultDataScheme } from '@visactor/vchart/esm/theme/color-scheme/builtin/default'
|
|
|
|
|
+import { getCurrencyDisplay } from '@/lib/currency'
|
|
|
import { formatChartTime, type TimeGranularity } from '@/lib/time'
|
|
import { formatChartTime, type TimeGranularity } from '@/lib/time'
|
|
|
import { MAX_CHART_TREND_POINTS } from '@/features/dashboard/constants'
|
|
import { MAX_CHART_TREND_POINTS } from '@/features/dashboard/constants'
|
|
|
import type {
|
|
import type {
|
|
@@ -10,6 +10,38 @@ import type {
|
|
|
|
|
|
|
|
type TFunction = (key: string) => string
|
|
type TFunction = (key: string) => string
|
|
|
|
|
|
|
|
|
|
+function getVChartDefaultColors(domainLength: number) {
|
|
|
|
|
+ const scheme =
|
|
|
|
|
+ vchartDefaultDataScheme.find(
|
|
|
|
|
+ (item) => !item.maxDomainLength || domainLength <= item.maxDomainLength
|
|
|
|
|
+ ) ?? vchartDefaultDataScheme[vchartDefaultDataScheme.length - 1]
|
|
|
|
|
+
|
|
|
|
|
+ return scheme.scheme
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function buildModelColorSpec(models: string[]) {
|
|
|
|
|
+ const domain = Array.from(new Set(models))
|
|
|
|
|
+ return {
|
|
|
|
|
+ type: 'ordinal',
|
|
|
|
|
+ domain,
|
|
|
|
|
+ range: getVChartDefaultColors(domain.length),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function renderQuotaCompat(rawQuota: number, digits = 4): string {
|
|
|
|
|
+ const { config, meta } = getCurrencyDisplay()
|
|
|
|
|
+ if (meta.kind === 'tokens') return rawQuota.toLocaleString()
|
|
|
|
|
+ const usd = rawQuota / config.quotaPerUnit
|
|
|
|
|
+ const rate = 'exchangeRate' in meta ? meta.exchangeRate : 1
|
|
|
|
|
+ const symbol = 'symbol' in meta ? meta.symbol : '$'
|
|
|
|
|
+ const value = usd * rate
|
|
|
|
|
+ const fixed = value.toFixed(digits)
|
|
|
|
|
+ if (parseFloat(fixed) === 0 && rawQuota > 0 && value > 0) {
|
|
|
|
|
+ return symbol + Math.pow(10, -digits).toFixed(digits)
|
|
|
|
|
+ }
|
|
|
|
|
+ return symbol + fixed
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Process and aggregate chart data
|
|
* Process and aggregate chart data
|
|
|
*/
|
|
*/
|
|
@@ -19,9 +51,61 @@ export function processChartData(
|
|
|
t?: TFunction
|
|
t?: TFunction
|
|
|
): ProcessedChartData {
|
|
): ProcessedChartData {
|
|
|
const tt: TFunction = t ?? ((x) => x)
|
|
const tt: TFunction = t ?? ((x) => x)
|
|
|
|
|
+ const otherLabel = tt('Other')
|
|
|
|
|
|
|
|
const formatInt = (value: number) =>
|
|
const formatInt = (value: number) =>
|
|
|
Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(value)
|
|
Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(value)
|
|
|
|
|
+ const formatQuotaValue = (value: number) => renderQuotaCompat(value, 4)
|
|
|
|
|
+ const formatQuotaTotal = (value: number) => renderQuotaCompat(value, 2)
|
|
|
|
|
+
|
|
|
|
|
+ const MAX_TOOLTIP_MODELS = 15
|
|
|
|
|
+
|
|
|
|
|
+ const makeTooltipDimensionUpdateContent = () => {
|
|
|
|
|
+ return (
|
|
|
|
|
+ array: Array<{
|
|
|
|
|
+ key: string
|
|
|
|
|
+ value: string | number
|
|
|
|
|
+ datum?: Record<string, unknown>
|
|
|
|
|
+ }>
|
|
|
|
|
+ ) => {
|
|
|
|
|
+ array.sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0))
|
|
|
|
|
+ let sum = 0
|
|
|
|
|
+ for (let i = 0; i < array.length; i++) {
|
|
|
|
|
+ if (array[i].key === 'Other' || array[i].key === otherLabel) continue
|
|
|
|
|
+ const v = Number(array[i].value) || 0
|
|
|
|
|
+ if (
|
|
|
|
|
+ array[i].datum &&
|
|
|
|
|
+ (array[i].datum as Record<string, unknown>)?.TimeSum
|
|
|
|
|
+ ) {
|
|
|
|
|
+ sum =
|
|
|
|
|
+ Number((array[i].datum as Record<string, unknown>)?.TimeSum) || sum
|
|
|
|
|
+ }
|
|
|
|
|
+ array[i].value = formatQuotaValue(v)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (array.length > MAX_TOOLTIP_MODELS) {
|
|
|
|
|
+ const visible = array.slice(0, MAX_TOOLTIP_MODELS)
|
|
|
|
|
+ let otherSum = 0
|
|
|
|
|
+ for (let i = MAX_TOOLTIP_MODELS; i < array.length; i++) {
|
|
|
|
|
+ const raw = array[i].datum
|
|
|
|
|
+ ? Number((array[i].datum as Record<string, unknown>)?.rawQuota) || 0
|
|
|
|
|
+ : 0
|
|
|
|
|
+ otherSum += raw
|
|
|
|
|
+ }
|
|
|
|
|
+ visible.push({
|
|
|
|
|
+ key: otherLabel,
|
|
|
|
|
+ value: formatQuotaValue(otherSum),
|
|
|
|
|
+ })
|
|
|
|
|
+ array = visible
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ array.unshift({
|
|
|
|
|
+ key: tt('Total:'),
|
|
|
|
|
+ value: formatQuotaValue(sum),
|
|
|
|
|
+ })
|
|
|
|
|
+ return array
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (!data || data.length === 0) {
|
|
if (!data || data.length === 0) {
|
|
|
return {
|
|
return {
|
|
@@ -35,7 +119,7 @@ export function processChartData(
|
|
|
categoryField: 'type',
|
|
categoryField: 'type',
|
|
|
title: {
|
|
title: {
|
|
|
visible: true,
|
|
visible: true,
|
|
|
- text: tt('Call Proportion'),
|
|
|
|
|
|
|
+ text: tt('Call Count Distribution'),
|
|
|
subtext: tt('No data available'),
|
|
subtext: tt('No data available'),
|
|
|
},
|
|
},
|
|
|
legends: { visible: false },
|
|
legends: { visible: false },
|
|
@@ -54,15 +138,15 @@ export function processChartData(
|
|
|
seriesField: 'Model',
|
|
seriesField: 'Model',
|
|
|
stack: true,
|
|
stack: true,
|
|
|
legends: { visible: true, selectMode: 'single' },
|
|
legends: { visible: true, selectMode: 'single' },
|
|
|
- title: {
|
|
|
|
|
- visible: true,
|
|
|
|
|
- text: tt('Quota Distribution'),
|
|
|
|
|
- subtext: `${tt('Total:')} ${formatQuotaWithCurrency(0, {
|
|
|
|
|
- digitsLarge: 2,
|
|
|
|
|
- digitsSmall: 2,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- })}`,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ spec_area: {
|
|
|
|
|
+ type: 'area',
|
|
|
|
|
+ data: [{ id: 'areaData', values: [] }],
|
|
|
|
|
+ xField: 'Time',
|
|
|
|
|
+ yField: 'Usage',
|
|
|
|
|
+ seriesField: 'Model',
|
|
|
|
|
+ stack: true,
|
|
|
|
|
+ legends: { visible: true, selectMode: 'single' },
|
|
|
},
|
|
},
|
|
|
spec_model_line: {
|
|
spec_model_line: {
|
|
|
type: 'line',
|
|
type: 'line',
|
|
@@ -86,10 +170,11 @@ export function processChartData(
|
|
|
legends: { visible: true, selectMode: 'single' },
|
|
legends: { visible: true, selectMode: 'single' },
|
|
|
title: {
|
|
title: {
|
|
|
visible: true,
|
|
visible: true,
|
|
|
- text: tt('Top Models'),
|
|
|
|
|
|
|
+ text: tt('Call Count Ranking'),
|
|
|
subtext: `${tt('Total:')} ${formatInt(0)}`,
|
|
subtext: `${tt('Total:')} ${formatInt(0)}`,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
|
|
+ totalQuotaDisplay: formatQuotaTotal(0),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -142,6 +227,7 @@ export function processChartData(
|
|
|
const allModels = Array.from(modelTotalsMap.keys())
|
|
const allModels = Array.from(modelTotalsMap.keys())
|
|
|
const sortedTimes = Array.from(timeModelMap.keys()).sort()
|
|
const sortedTimes = Array.from(timeModelMap.keys()).sort()
|
|
|
const sortedModels = [...allModels].sort()
|
|
const sortedModels = [...allModels].sort()
|
|
|
|
|
+ const modelColor = buildModelColorSpec([...sortedModels, otherLabel])
|
|
|
|
|
|
|
|
// Pad time points if too few (default 7 points)
|
|
// Pad time points if too few (default 7 points)
|
|
|
const MAX_TREND_POINTS = MAX_CHART_TREND_POINTS
|
|
const MAX_TREND_POINTS = MAX_CHART_TREND_POINTS
|
|
@@ -166,14 +252,6 @@ export function processChartData(
|
|
|
}
|
|
}
|
|
|
const chartTimes = fillTimePoints(sortedTimes)
|
|
const chartTimes = fillTimePoints(sortedTimes)
|
|
|
|
|
|
|
|
- const modelColorMap = sortedModels.reduce<Record<string, string>>(
|
|
|
|
|
- (acc, model, index) => {
|
|
|
|
|
- acc[model] = getChartColor(index)
|
|
|
|
|
- return acc
|
|
|
|
|
- },
|
|
|
|
|
- {}
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
const totalTimes = Array.from(modelTotalsMap.values()).reduce(
|
|
const totalTimes = Array.from(modelTotalsMap.values()).reduce(
|
|
|
(sum, x) => sum + (Number(x.count) || 0),
|
|
(sum, x) => sum + (Number(x.count) || 0),
|
|
|
0
|
|
0
|
|
@@ -223,14 +301,70 @@ export function processChartData(
|
|
|
})
|
|
})
|
|
|
lineValues.sort((a, b) => a.Time.localeCompare(b.Time))
|
|
lineValues.sort((a, b) => a.Time.localeCompare(b.Time))
|
|
|
|
|
|
|
|
- // Line chart: model call trend
|
|
|
|
|
|
|
+ // Area chart: top models by quota + "Other" bucket (too many series = unreadable)
|
|
|
|
|
+ const MAX_AREA_MODELS = 15
|
|
|
|
|
+ const rankedQuotaModels = Array.from(modelTotalsMap.entries())
|
|
|
|
|
+ .map(([model, stats]) => ({
|
|
|
|
|
+ Model: model,
|
|
|
|
|
+ Quota: Number(stats.quota) || 0,
|
|
|
|
|
+ }))
|
|
|
|
|
+ .sort((a, b) => b.Quota - a.Quota)
|
|
|
|
|
+ const topAreaModels = new Set(
|
|
|
|
|
+ rankedQuotaModels.slice(0, MAX_AREA_MODELS).map((m) => m.Model)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ const areaValues: typeof lineValues = []
|
|
|
|
|
+ chartTimes.forEach((time) => {
|
|
|
|
|
+ const buckets = new Map<string, { rawQuota: number; usage: number }>()
|
|
|
|
|
+ const modelMap = timeModelMap.get(time)
|
|
|
|
|
+ let timeSum = 0
|
|
|
|
|
+ sortedModels.forEach((model) => {
|
|
|
|
|
+ const stats = modelMap?.get(model)
|
|
|
|
|
+ const rawQuota = Number(stats?.quota) || 0
|
|
|
|
|
+ const usd = rawQuota ? rawQuota / quotaPerUnit : 0
|
|
|
|
|
+ const usage = usd ? Number(usd.toFixed(4)) : 0
|
|
|
|
|
+ timeSum += rawQuota
|
|
|
|
|
+ const key = topAreaModels.has(model) ? model : otherLabel
|
|
|
|
|
+ const prev = buckets.get(key) || { rawQuota: 0, usage: 0 }
|
|
|
|
|
+ buckets.set(key, {
|
|
|
|
|
+ rawQuota: prev.rawQuota + rawQuota,
|
|
|
|
|
+ usage: Number((prev.usage + usage).toFixed(4)),
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ for (const [model, vals] of buckets) {
|
|
|
|
|
+ areaValues.push({
|
|
|
|
|
+ Time: time,
|
|
|
|
|
+ Model: model,
|
|
|
|
|
+ rawQuota: vals.rawQuota,
|
|
|
|
|
+ Usage: vals.usage,
|
|
|
|
|
+ TimeSum: timeSum,
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ areaValues.sort((a, b) => a.Time.localeCompare(b.Time))
|
|
|
|
|
+
|
|
|
|
|
+ // Line chart: model call trend (top models + "Other" bucket)
|
|
|
|
|
+ const MAX_TREND_MODELS = 20
|
|
|
|
|
+ const rankedTrendModels = Array.from(modelTotalsMap.entries())
|
|
|
|
|
+ .map(([model, stats]) => ({
|
|
|
|
|
+ Model: model,
|
|
|
|
|
+ Count: Number(stats.count) || 0,
|
|
|
|
|
+ }))
|
|
|
|
|
+ .sort((a, b) => b.Count - a.Count)
|
|
|
|
|
+ const topTrendModels = rankedTrendModels
|
|
|
|
|
+ .slice(0, MAX_TREND_MODELS)
|
|
|
|
|
+ .map((item) => item.Model)
|
|
|
|
|
+ const otherTrendModels = rankedTrendModels
|
|
|
|
|
+ .slice(MAX_TREND_MODELS)
|
|
|
|
|
+ .map((item) => item.Model)
|
|
|
|
|
+
|
|
|
const modelLineValues: Array<{
|
|
const modelLineValues: Array<{
|
|
|
Time: string
|
|
Time: string
|
|
|
Model: string
|
|
Model: string
|
|
|
Count: number
|
|
Count: number
|
|
|
}> = []
|
|
}> = []
|
|
|
chartTimes.forEach((time) => {
|
|
chartTimes.forEach((time) => {
|
|
|
- const timeData = sortedModels.map((model) => {
|
|
|
|
|
|
|
+ const timeData = topTrendModels.map((model) => {
|
|
|
const stats = timeModelMap.get(time)?.get(model)
|
|
const stats = timeModelMap.get(time)?.get(model)
|
|
|
return {
|
|
return {
|
|
|
Time: time,
|
|
Time: time,
|
|
@@ -238,6 +372,17 @@ export function processChartData(
|
|
|
Count: Number(stats?.count) || 0,
|
|
Count: Number(stats?.count) || 0,
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
+ if (otherTrendModels.length > 0) {
|
|
|
|
|
+ const otherCount = otherTrendModels.reduce((sum, model) => {
|
|
|
|
|
+ const stats = timeModelMap.get(time)?.get(model)
|
|
|
|
|
+ return sum + (Number(stats?.count) || 0)
|
|
|
|
|
+ }, 0)
|
|
|
|
|
+ timeData.push({
|
|
|
|
|
+ Time: time,
|
|
|
|
|
+ Model: otherLabel,
|
|
|
|
|
+ Count: otherCount,
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
modelLineValues.push(...timeData)
|
|
modelLineValues.push(...timeData)
|
|
|
})
|
|
})
|
|
|
modelLineValues.sort((a, b) => a.Time.localeCompare(b.Time))
|
|
modelLineValues.sort((a, b) => a.Time.localeCompare(b.Time))
|
|
@@ -257,7 +402,7 @@ export function processChartData(
|
|
|
const otherCount = allRankValues
|
|
const otherCount = allRankValues
|
|
|
.slice(MAX_RANK_MODELS)
|
|
.slice(MAX_RANK_MODELS)
|
|
|
.reduce((sum, item) => sum + item.Count, 0)
|
|
.reduce((sum, item) => sum + item.Count, 0)
|
|
|
- rankValues = [...topModels, { Model: tt('Other'), Count: otherCount }]
|
|
|
|
|
|
|
+ rankValues = [...topModels, { Model: otherLabel, Count: otherCount }]
|
|
|
} else {
|
|
} else {
|
|
|
rankValues = allRankValues
|
|
rankValues = allRankValues
|
|
|
}
|
|
}
|
|
@@ -280,11 +425,12 @@ export function processChartData(
|
|
|
},
|
|
},
|
|
|
title: {
|
|
title: {
|
|
|
visible: true,
|
|
visible: true,
|
|
|
- text: tt('Call Proportion'),
|
|
|
|
|
|
|
+ text: tt('Call Count Distribution'),
|
|
|
subtext: `${tt('Total:')} ${formatInt(totalTimes)}`,
|
|
subtext: `${tt('Total:')} ${formatInt(totalTimes)}`,
|
|
|
},
|
|
},
|
|
|
legends: { visible: true, orient: 'left' },
|
|
legends: { visible: true, orient: 'left' },
|
|
|
label: { visible: true },
|
|
label: { visible: true },
|
|
|
|
|
+ color: modelColor,
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
mark: {
|
|
mark: {
|
|
|
content: [
|
|
content: [
|
|
@@ -296,7 +442,6 @@ export function processChartData(
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- color: { specified: modelColorMap },
|
|
|
|
|
background: { fill: 'transparent' },
|
|
background: { fill: 'transparent' },
|
|
|
animation: true,
|
|
animation: true,
|
|
|
},
|
|
},
|
|
@@ -308,15 +453,7 @@ export function processChartData(
|
|
|
seriesField: 'Model',
|
|
seriesField: 'Model',
|
|
|
stack: true,
|
|
stack: true,
|
|
|
legends: { visible: true, selectMode: 'single' },
|
|
legends: { visible: true, selectMode: 'single' },
|
|
|
- title: {
|
|
|
|
|
- visible: true,
|
|
|
|
|
- text: tt('Quota Distribution'),
|
|
|
|
|
- subtext: `${tt('Total:')} ${formatQuotaWithCurrency(totalQuotaRaw, {
|
|
|
|
|
- digitsLarge: 2,
|
|
|
|
|
- digitsSmall: 2,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- })}`,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ color: modelColor,
|
|
|
bar: {
|
|
bar: {
|
|
|
state: {
|
|
state: {
|
|
|
hover: { stroke: '#000', lineWidth: 1 },
|
|
hover: { stroke: '#000', lineWidth: 1 },
|
|
@@ -328,11 +465,7 @@ export function processChartData(
|
|
|
{
|
|
{
|
|
|
key: (datum: Record<string, unknown>) => datum?.Model,
|
|
key: (datum: Record<string, unknown>) => datum?.Model,
|
|
|
value: (datum: Record<string, unknown>) =>
|
|
value: (datum: Record<string, unknown>) =>
|
|
|
- formatQuotaWithCurrency(Number(datum?.rawQuota) || 0, {
|
|
|
|
|
- digitsLarge: 4,
|
|
|
|
|
- digitsSmall: 4,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- }),
|
|
|
|
|
|
|
+ formatQuotaValue(Number(datum?.rawQuota) || 0),
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
@@ -344,58 +477,67 @@ export function processChartData(
|
|
|
Number(datum?.rawQuota) || 0,
|
|
Number(datum?.rawQuota) || 0,
|
|
|
},
|
|
},
|
|
|
],
|
|
],
|
|
|
- updateContent: (
|
|
|
|
|
- array: Array<{
|
|
|
|
|
- key: string
|
|
|
|
|
- value: string | number
|
|
|
|
|
- datum?: Record<string, unknown>
|
|
|
|
|
- }>
|
|
|
|
|
- ) => {
|
|
|
|
|
- array.sort(
|
|
|
|
|
- (a, b) => (Number(b.value) || 0) - (Number(a.value) || 0)
|
|
|
|
|
- )
|
|
|
|
|
- let sum = 0
|
|
|
|
|
- for (let i = 0; i < array.length; i++) {
|
|
|
|
|
- if (array[i].key === 'Other') continue
|
|
|
|
|
- const v = Number(array[i].value) || 0
|
|
|
|
|
- if (
|
|
|
|
|
- array[i].datum &&
|
|
|
|
|
- (array[i].datum as Record<string, unknown>)?.TimeSum
|
|
|
|
|
- ) {
|
|
|
|
|
- sum =
|
|
|
|
|
- Number(
|
|
|
|
|
- (array[i].datum as Record<string, unknown>)?.TimeSum
|
|
|
|
|
- ) || sum
|
|
|
|
|
- }
|
|
|
|
|
- array[i].value = formatQuotaWithCurrency(v, {
|
|
|
|
|
- digitsLarge: 4,
|
|
|
|
|
- digitsSmall: 4,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- array.unshift({
|
|
|
|
|
- key: tt('Total:'),
|
|
|
|
|
- value: formatQuotaWithCurrency(sum, {
|
|
|
|
|
- digitsLarge: 4,
|
|
|
|
|
- digitsSmall: 4,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- }),
|
|
|
|
|
- })
|
|
|
|
|
- return array
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ updateContent: makeTooltipDimensionUpdateContent(),
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- color: { specified: modelColorMap },
|
|
|
|
|
|
|
+ background: { fill: 'transparent' },
|
|
|
|
|
+ animation: true,
|
|
|
|
|
+ },
|
|
|
|
|
+ spec_area: {
|
|
|
|
|
+ type: 'area',
|
|
|
|
|
+ data: [{ id: 'areaData', values: areaValues }],
|
|
|
|
|
+ xField: 'Time',
|
|
|
|
|
+ yField: 'Usage',
|
|
|
|
|
+ seriesField: 'Model',
|
|
|
|
|
+ stack: false,
|
|
|
|
|
+ legends: { visible: true, selectMode: 'single' },
|
|
|
|
|
+ color: modelColor,
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ mark: {
|
|
|
|
|
+ content: [
|
|
|
|
|
+ {
|
|
|
|
|
+ key: (datum: Record<string, unknown>) => datum?.Model,
|
|
|
|
|
+ value: (datum: Record<string, unknown>) =>
|
|
|
|
|
+ formatQuotaValue(Number(datum?.rawQuota) || 0),
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ },
|
|
|
|
|
+ dimension: {
|
|
|
|
|
+ content: [
|
|
|
|
|
+ {
|
|
|
|
|
+ key: (datum: Record<string, unknown>) => datum?.Model,
|
|
|
|
|
+ value: (datum: Record<string, unknown>) =>
|
|
|
|
|
+ Number(datum?.rawQuota) || 0,
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ updateContent: makeTooltipDimensionUpdateContent(),
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ area: {
|
|
|
|
|
+ style: {
|
|
|
|
|
+ fillOpacity: 0.08,
|
|
|
|
|
+ curveType: 'monotone',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ line: {
|
|
|
|
|
+ style: {
|
|
|
|
|
+ lineWidth: 2,
|
|
|
|
|
+ curveType: 'monotone',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ point: { visible: false },
|
|
|
background: { fill: 'transparent' },
|
|
background: { fill: 'transparent' },
|
|
|
animation: true,
|
|
animation: true,
|
|
|
},
|
|
},
|
|
|
spec_model_line: {
|
|
spec_model_line: {
|
|
|
- type: 'line',
|
|
|
|
|
|
|
+ type: 'area',
|
|
|
data: [{ id: 'lineData', values: modelLineValues }],
|
|
data: [{ id: 'lineData', values: modelLineValues }],
|
|
|
xField: 'Time',
|
|
xField: 'Time',
|
|
|
yField: 'Count',
|
|
yField: 'Count',
|
|
|
seriesField: 'Model',
|
|
seriesField: 'Model',
|
|
|
|
|
+ stack: false,
|
|
|
legends: { visible: true, selectMode: 'single' },
|
|
legends: { visible: true, selectMode: 'single' },
|
|
|
|
|
+ color: modelColor,
|
|
|
title: {
|
|
title: {
|
|
|
visible: true,
|
|
visible: true,
|
|
|
text: tt('Call Trend'),
|
|
text: tt('Call Trend'),
|
|
@@ -442,7 +584,18 @@ export function processChartData(
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- color: { specified: modelColorMap },
|
|
|
|
|
|
|
+ area: {
|
|
|
|
|
+ style: {
|
|
|
|
|
+ fillOpacity: 0.08,
|
|
|
|
|
+ curveType: 'monotone',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ line: {
|
|
|
|
|
+ style: {
|
|
|
|
|
+ lineWidth: 2,
|
|
|
|
|
+ curveType: 'monotone',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
point: { visible: false },
|
|
point: { visible: false },
|
|
|
background: { fill: 'transparent' },
|
|
background: { fill: 'transparent' },
|
|
|
animation: true,
|
|
animation: true,
|
|
@@ -454,9 +607,10 @@ export function processChartData(
|
|
|
yField: 'Count',
|
|
yField: 'Count',
|
|
|
seriesField: 'Model',
|
|
seriesField: 'Model',
|
|
|
legends: { visible: true, selectMode: 'single' },
|
|
legends: { visible: true, selectMode: 'single' },
|
|
|
|
|
+ color: modelColor,
|
|
|
title: {
|
|
title: {
|
|
|
visible: true,
|
|
visible: true,
|
|
|
- text: tt('Top Models'),
|
|
|
|
|
|
|
+ text: tt('Call Count Ranking'),
|
|
|
subtext: `${tt('Total:')} ${formatInt(totalTimes)}`,
|
|
subtext: `${tt('Total:')} ${formatInt(totalTimes)}`,
|
|
|
},
|
|
},
|
|
|
bar: {
|
|
bar: {
|
|
@@ -475,10 +629,10 @@ export function processChartData(
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
- color: { specified: modelColorMap },
|
|
|
|
|
background: { fill: 'transparent' },
|
|
background: { fill: 'transparent' },
|
|
|
animation: true,
|
|
animation: true,
|
|
|
},
|
|
},
|
|
|
|
|
+ totalQuotaDisplay: formatQuotaTotal(totalQuotaRaw),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -505,12 +659,7 @@ export function processUserChartData(
|
|
|
const { config } = getCurrencyDisplay()
|
|
const { config } = getCurrencyDisplay()
|
|
|
const quotaPerUnit = config.quotaPerUnit
|
|
const quotaPerUnit = config.quotaPerUnit
|
|
|
|
|
|
|
|
- const formatVal = (raw: number) =>
|
|
|
|
|
- formatQuotaWithCurrency(raw, {
|
|
|
|
|
- digitsLarge: 2,
|
|
|
|
|
- digitsSmall: 2,
|
|
|
|
|
- abbreviate: false,
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const formatVal = (raw: number) => renderQuotaCompat(raw, 2)
|
|
|
|
|
|
|
|
const emptyResult: ProcessedUserChartData = {
|
|
const emptyResult: ProcessedUserChartData = {
|
|
|
spec_user_rank: {
|
|
spec_user_rank: {
|