@@ -18,13 +18,17 @@ import {
1818const PROGRESS_MID = 25 ;
1919const VOLUME_MID = 25 ;
2020
21- const QUADRANT_COLORS : Record < string , string > = {
22- 'Volume Focus' : '#ef4444' ,
23- 'Optimal Growth' : '#22c55e' ,
24- 'Undertrained' : '#f59e0b' ,
25- 'Strength Focus' : '#3b82f6' ,
21+ const SCORE_COLORS = [ '#ef4444' , '#f59e0b' , '#22c55e' ] as const ;
22+
23+ const getDotColor = ( total : number ) => {
24+ if ( total <= 30 ) return SCORE_COLORS [ 0 ] ;
25+ if ( total <= 60 ) return SCORE_COLORS [ 1 ] ;
26+ return SCORE_COLORS [ 2 ] ;
2627} ;
2728
29+ const volColor = ( v : number ) => v <= 15 ? '#ef4444' : v <= 35 ? '#f59e0b' : '#22c55e' ;
30+ const progColor = ( v : number ) => v <= 11 ? '#ef4444' : v <= 22 ? '#f59e0b' : '#22c55e' ;
31+
2832interface ChartPoint {
2933 name : string ;
3034 muscleId : string ;
@@ -100,24 +104,26 @@ export const HypertrophyScatterCard: React.FC<HypertrophyScatterCardProps> = ({
100104 const d : ChartPoint | undefined = payload [ 0 ] ?. payload ;
101105 if ( ! d ) return null ;
102106
103- const quadrantDesc : Record < string , string > = {
104- 'Volume Focus' : 'High volume but lagging strength progress, add progressive overload' ,
105- 'Optimal Growth' : 'High volume + strong progress, ideal for hypertrophy' ,
106- 'Undertrained' : 'Low volume + low progress, increase training frequency' ,
107- 'Strength Focus' : 'Strong progress despite low volume, great strength adaptation' ,
107+ const quadrantDesc : Record < string , { desc : string ; advice : string } > = {
108+ 'Volume Focus' : { desc : 'High volume but lagging strength progress' , advice : 'Focus on progressive overload, add weight or reps slowly' } ,
109+ 'Optimal Growth' : { desc : 'High volume + strong progress, ideal for hypertrophy' , advice : 'Keep it up! Maintain this balance for gains' } ,
110+ 'Undertrained' : { desc : 'Low volume + low progress' , advice : 'If prioritizing this muscle, add sets and train 2-3x/week' } ,
111+ 'Strength Focus' : { desc : 'Strong progress despite low volume' , advice : 'Consider increasing volume for more size gains' } ,
108112 } ;
109113
114+ const q = quadrantDesc [ d . quadrant ] ;
110115 return (
111116 < div className = "rounded-lg px-3 py-2 shadow-2xl border text-xs"
112117 style = { { backgroundColor : 'rgb(var(--panel-rgb) / 0.95)' , borderColor : 'rgb(var(--border-rgb) / 0.5)' , color : 'var(--text-primary)' } } >
113118 < p className = "font-semibold mb-1.5" > { d . name } < span className = "opacity-60 font-normal" > ({ d . total } /100)</ span > </ p >
114119 < div className = "flex items-center gap-3 mb-1.5" >
115- < span style = { { color : '#3b82f6' } } > Progress < b > { d . progress } /40</ b > </ span >
116- < span style = { { color : '#22c55e' } } > Volume < b > { d . volume } /50</ b > </ span >
120+ < span > Progress < b style = { { color : progColor ( d . progress ) } } > { d . progress } /40</ b > </ span >
121+ < span > Volume < b style = { { color : volColor ( d . volume ) } } > { d . volume } /50</ b > </ span >
117122 </ div >
118- < div className = "border-t pt-1.5" style = { { borderColor : 'rgb(var(--border-rgb) / 0.3)' } } >
119- < p className = "font-semibold text-[10px]" style = { { color : QUADRANT_COLORS [ d . quadrant ] } } > { d . quadrant } </ p >
120- < p className = "text-[9px] opacity-70 leading-tight mt-0.5" > { quadrantDesc [ d . quadrant ] } </ p >
123+ < div className = "border-t pt-1.5 text-slate-400" style = { { borderColor : 'rgb(var(--border-rgb) / 0.3)' } } >
124+ < p className = "font-semibold text-[10px]" > { d . quadrant } </ p >
125+ < p className = "text-[9px] leading-tight mt-0.5" > { q . desc } </ p >
126+ < p className = "text-[8px] mt-1" > { q . advice } </ p >
121127 </ div >
122128 </ div >
123129 ) ;
@@ -142,7 +148,7 @@ export const HypertrophyScatterCard: React.FC<HypertrophyScatterCardProps> = ({
142148 </ div >
143149 </ div >
144150
145- < div className = "flex-1 px-3 pb-3 relative" style = { { minHeight : 350 } } >
151+ < div className = "flex-1 w-full relative" style = { { minHeight : 300 } } >
146152 { hypertrophyData . length > 0 ? (
147153 < >
148154 < ResponsiveContainer width = "100%" height = "100%" >
@@ -160,16 +166,19 @@ export const HypertrophyScatterCard: React.FC<HypertrophyScatterCardProps> = ({
160166 < RechartsTooltip content = { < CustomScatterTooltip /> } />
161167
162168 < Scatter data = { chartData } shape = "circle" isAnimationActive = { false } >
163- { chartData . map ( ( entry ) => (
164- < Cell
165- key = { entry . muscleId }
166- fill = { QUADRANT_COLORS [ entry . quadrant ] }
167- fillOpacity = { 0.85 }
168- stroke = { QUADRANT_COLORS [ entry . quadrant ] }
169- strokeWidth = { 0.5 }
170- style = { { cursor : 'pointer' } }
171- />
172- ) ) }
169+ { chartData . map ( ( entry ) => {
170+ const c = getDotColor ( entry . total ) ;
171+ return (
172+ < Cell
173+ key = { entry . muscleId }
174+ fill = { c }
175+ fillOpacity = { 0.85 }
176+ stroke = { c }
177+ strokeWidth = { 0.5 }
178+ style = { { cursor : 'pointer' } }
179+ />
180+ ) ;
181+ } ) }
173182 </ Scatter >
174183
175184 < Scatter data = { chartData . filter ( d => labeledIds . includes ( d . muscleId ) ) } shape = "circle" isAnimationActive = { false } legendType = "none"
@@ -187,6 +196,7 @@ export const HypertrophyScatterCard: React.FC<HypertrophyScatterCardProps> = ({
187196 < div className = "text-[10px] text-slate-500 py-4 text-center" > No muscle data available.</ div >
188197 ) }
189198 </ div >
199+
190200 </ div >
191201 ) ;
192202} ;
0 commit comments