@@ -86,7 +86,7 @@ fn render_month_summary(frame: &mut Frame<'_>, area: Rect, state: &AppState, the
8686 let ( year, month) = state. stats . current_month ;
8787 let month_name = month_name ( month) ;
8888
89- let card = Card :: new ( "Month Summary" , theme) ;
89+ let card = Card :: new ( "Month Summary" , theme) . focused ( true ) ;
9090 let inner = card. inner ( area) ;
9191 card. render_frame ( frame, area) ;
9292
@@ -325,11 +325,18 @@ fn render_category_breakdown(frame: &mut Frame<'_>, area: Rect, state: &AppState
325325 . take ( inner. height as usize )
326326 . map ( |( category, amount) | {
327327 let pct = compute_percentage ( * amount, total) ;
328+ let style = if pct >= 75 {
329+ BarStyle :: Block
330+ } else if pct >= 25 {
331+ BarStyle :: Line
332+ } else {
333+ BarStyle :: Dot
334+ } ;
328335 let bar = ascii_bar_styled (
329336 amount. saturating_abs ( ) as u64 ,
330337 total. saturating_abs ( ) as u64 ,
331338 20 ,
332- BarStyle :: Line ,
339+ style ,
333340 ) ;
334341
335342 Line :: from ( vec ! [
@@ -379,6 +386,27 @@ fn render_monthly_trend(frame: &mut Frame<'_>, area: Rect, state: &AppState, the
379386 . iter ( )
380387 . map ( |( label, value) | ( label. as_str ( ) , ( * value) . max ( 0 ) as u64 ) )
381388 . collect ( ) ;
389+ let income_color = if income_data. is_empty ( ) {
390+ theme. dim
391+ } else if income_data. last ( ) . map ( |( _, v) | * v) . unwrap_or ( 0 )
392+ >= income_data. first ( ) . map ( |( _, v) | * v) . unwrap_or ( 0 )
393+ {
394+ theme. positive
395+ } else {
396+ theme. warning
397+ } ;
398+ let income_title = Line :: from ( vec ! [
399+ Span :: styled( "Income (6m)" , Style :: default ( ) . fg( theme. text_muted) ) ,
400+ Span :: raw( " " ) ,
401+ Span :: styled(
402+ if income_color == theme. positive {
403+ "●"
404+ } else {
405+ "○"
406+ } ,
407+ Style :: default ( ) . fg( income_color) ,
408+ ) ,
409+ ] ) ;
382410 if income_data. is_empty ( ) {
383411 let card = Card :: new ( "Income (6m)" , theme) ;
384412 let inner = card. inner ( layout[ 0 ] ) ;
@@ -392,13 +420,43 @@ fn render_monthly_trend(frame: &mut Frame<'_>, area: Rect, state: &AppState, the
392420 inner,
393421 ) ;
394422 } else {
395- render_bar_chart ( frame, layout[ 0 ] , "Income (6m)" , & income_data, theme) ;
423+ let card = Card :: new ( "Income (6m)" , theme) . focused ( true ) ;
424+ let inner = card. inner ( layout[ 0 ] ) ;
425+ card. render_frame ( frame, layout[ 0 ] ) ;
426+ frame. render_widget ( Paragraph :: new ( income_title) , Rect { height : 1 , ..inner } ) ;
427+ let chart_area = Rect {
428+ y : inner. y + 1 ,
429+ height : inner. height . saturating_sub ( 1 ) ,
430+ ..inner
431+ } ;
432+ render_bar_chart ( frame, chart_area, "" , & income_data, theme) ;
396433 }
397434
398435 let expense_data: Vec < ( & str , u64 ) > = expense_trend
399436 . iter ( )
400437 . map ( |( label, value) | ( label. as_str ( ) , ( * value) . max ( 0 ) as u64 ) )
401438 . collect ( ) ;
439+ let expense_color = if expense_data. is_empty ( ) {
440+ theme. dim
441+ } else if expense_data. last ( ) . map ( |( _, v) | * v) . unwrap_or ( 0 )
442+ >= expense_data. first ( ) . map ( |( _, v) | * v) . unwrap_or ( 0 )
443+ {
444+ theme. warning
445+ } else {
446+ theme. positive
447+ } ;
448+ let expense_title = Line :: from ( vec ! [
449+ Span :: styled( "Expenses (6m)" , Style :: default ( ) . fg( theme. text_muted) ) ,
450+ Span :: raw( " " ) ,
451+ Span :: styled(
452+ if expense_color == theme. warning {
453+ "●"
454+ } else {
455+ "○"
456+ } ,
457+ Style :: default ( ) . fg( expense_color) ,
458+ ) ,
459+ ] ) ;
402460 if expense_data. is_empty ( ) {
403461 let card = Card :: new ( "Expenses (6m)" , theme) ;
404462 let inner = card. inner ( layout[ 1 ] ) ;
@@ -412,7 +470,16 @@ fn render_monthly_trend(frame: &mut Frame<'_>, area: Rect, state: &AppState, the
412470 inner,
413471 ) ;
414472 } else {
415- render_bar_chart ( frame, layout[ 1 ] , "Expenses (6m)" , & expense_data, theme) ;
473+ let card = Card :: new ( "Expenses (6m)" , theme) . focused ( true ) ;
474+ let inner = card. inner ( layout[ 1 ] ) ;
475+ card. render_frame ( frame, layout[ 1 ] ) ;
476+ frame. render_widget ( Paragraph :: new ( expense_title) , Rect { height : 1 , ..inner } ) ;
477+ let chart_area = Rect {
478+ y : inner. y + 1 ,
479+ height : inner. height . saturating_sub ( 1 ) ,
480+ ..inner
481+ } ;
482+ render_bar_chart ( frame, chart_area, "" , & expense_data, theme) ;
416483 }
417484}
418485
0 commit comments