= {}
+ const adaptiveVal = params.find((p) => p.seriesName === 'Adaptive')?.value ?? 0
+ const classicVal = params.find((p) => p.seriesName === 'Classic')?.value ?? 0
+ const diff = adaptiveVal - classicVal
+ const diffStr =
+ diff > 0
+ ? `+${diff}pp`
+ : diff < 0
+ ? `${diff}pp`
+ : '0pp'
for (const p of params) {
- const isAdaptive = p.seriesName.includes('(Adaptive)')
- const skillLabel = p.seriesName.replace(' (Adaptive)', '').replace(' (Classic)', '')
- if (!bySkill[skillLabel]) {
- bySkill[skillLabel] = { color: p.color }
- }
- if (isAdaptive) {
- bySkill[skillLabel].adaptive = p.value
- } else {
- bySkill[skillLabel].classic = p.value
- }
- }
-
- for (const [label, values] of Object.entries(bySkill)) {
- const diff =
- values.adaptive !== undefined && values.classic !== undefined
- ? values.adaptive - values.classic
- : 0
- const diffStr =
- diff > 0
- ? `+${diff}`
- : diff < 0
- ? `${diff}`
- : '0'
- html += `${label}: ${values.adaptive ?? '—'}% vs ${values.classic ?? '—'}% (${diffStr})
`
+ html += `${p.marker} ${p.seriesName}: ${p.value}%
`
}
+ html += `Advantage: ${diffStr}`
return html
},
},
legend: {
data: [
- { name: 'Adaptive (solid)', icon: 'line' },
- { name: 'Classic (dashed)', icon: 'line' },
+ { name: 'Adaptive', itemStyle: { color: '#22c55e' } },
+ { name: 'Classic', itemStyle: { color: '#6b7280' } },
],
bottom: 0,
- textStyle: { color: '#9ca3af', fontSize: 11 },
+ textStyle: { color: '#9ca3af', fontSize: 12 },
},
grid: {
left: '3%',
@@ -732,16 +675,71 @@ function MultiSkillTrajectoryChart({ data }: { data: TrajectoryData | null }) {
},
yAxis: {
type: 'value',
- name: 'Mastery %',
+ name: 'Average Mastery %',
nameLocation: 'middle',
- nameGap: 40,
+ nameGap: 45,
min: 0,
max: 100,
axisLabel: { color: '#9ca3af', formatter: '{value}%' },
axisLine: { lineStyle: { color: '#374151' } },
splitLine: { lineStyle: { color: '#374151', type: 'dashed' } },
},
- series,
+ series: [
+ {
+ name: 'Adaptive',
+ type: 'line',
+ data: adaptiveAvg,
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ lineStyle: { color: '#22c55e', width: 3 },
+ itemStyle: { color: '#22c55e' },
+ areaStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: 'rgba(34, 197, 94, 0.3)' },
+ { offset: 1, color: 'rgba(34, 197, 94, 0.05)' },
+ ],
+ },
+ },
+ markLine: {
+ silent: true,
+ lineStyle: { color: '#374151', type: 'dashed' },
+ data: [
+ { yAxis: 50, label: { formatter: '50%', color: '#9ca3af' } },
+ { yAxis: 80, label: { formatter: '80%', color: '#9ca3af' } },
+ ],
+ },
+ },
+ {
+ name: 'Classic',
+ type: 'line',
+ data: classicAvg,
+ smooth: true,
+ symbol: 'circle',
+ symbolSize: 8,
+ lineStyle: { color: '#6b7280', width: 3 },
+ itemStyle: { color: '#6b7280' },
+ areaStyle: {
+ color: {
+ type: 'linear',
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 1,
+ colorStops: [
+ { offset: 0, color: 'rgba(107, 114, 128, 0.2)' },
+ { offset: 1, color: 'rgba(107, 114, 128, 0.02)' },
+ ],
+ },
+ },
+ },
+ ],
}
return (
@@ -749,13 +747,13 @@ function MultiSkillTrajectoryChart({ data }: { data: TrajectoryData | null }) {
- Adaptive vs Classic: All Skills
+ Average Mastery: Adaptive vs Classic
- Solid lines = Adaptive mode, Dashed lines = Classic mode. Same color = same skill. Adaptive
- consistently reaches mastery faster.
+ Average mastery across {numSkills} deficient skills. Adaptive mode (green) consistently
+ outpaces Classic mode (gray), reaching 80% mastery faster.
-
+
)
}