use gpui::{Entity, Render}; use ui::{ProgressBar, prelude::*}; #[derive(RegisterComponent)] pub struct UserSpending { free_tier_current: u32, free_tier_cap: u32, over_tier_current: u32, over_tier_cap: u32, free_tier_progress: Entity, over_tier_progress: Entity, } impl UserSpending { pub fn new( free_tier_current: u32, free_tier_cap: u32, over_tier_current: u32, over_tier_cap: u32, cx: &mut App, ) -> Self { let free_tier_capped = free_tier_current == free_tier_cap; let free_tier_near_capped = free_tier_current as f32 / 100.0 >= free_tier_cap as f32 / 100.0 * 0.9; let over_tier_capped = over_tier_current == over_tier_cap; let over_tier_near_capped = over_tier_current as f32 / 100.0 >= over_tier_cap as f32 / 100.0 * 0.9; let free_tier_progress = cx.new(|cx| { ProgressBar::new( "free_tier", free_tier_current as f32, free_tier_cap as f32, cx, ) }); let over_tier_progress = cx.new(|cx| { ProgressBar::new( "over_tier", over_tier_current as f32, over_tier_cap as f32, cx, ) }); if free_tier_capped { free_tier_progress.update(cx, |progress_bar, cx| { progress_bar.fg_color(cx.theme().status().error); }); } else if free_tier_near_capped { free_tier_progress.update(cx, |progress_bar, cx| { progress_bar.fg_color(cx.theme().status().warning); }); } if over_tier_capped { over_tier_progress.update(cx, |progress_bar, cx| { progress_bar.fg_color(cx.theme().status().error); }); } else if over_tier_near_capped { over_tier_progress.update(cx, |progress_bar, cx| { progress_bar.fg_color(cx.theme().status().warning); }); } Self { free_tier_current, free_tier_cap, over_tier_current, over_tier_cap, free_tier_progress, over_tier_progress, } } } impl Render for UserSpending { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let formatted_free_tier = format!( "${} / ${}", self.free_tier_current as f32 / 100.0, self.free_tier_cap as f32 / 100.0 ); let formatted_over_tier = format!( "${} / ${}", self.over_tier_current as f32 / 100.0, self.over_tier_cap as f32 / 100.0 ); v_group() .elevation_2(cx) .py_1p5() .px_2p5() .w(px(360.)) .child( v_flex() .child( v_flex() .p_1p5() .gap_0p5() .child( h_flex() .justify_between() .child(Label::new("Free Tier Usage").size(LabelSize::Small)) .child( Label::new(formatted_free_tier) .size(LabelSize::Small) .color(Color::Muted), ), ) .child(self.free_tier_progress.clone()), ) .child( v_flex() .p_1p5() .gap_0p5() .child( h_flex() .justify_between() .child(Label::new("Current Spending").size(LabelSize::Small)) .child( Label::new(formatted_over_tier) .size(LabelSize::Small) .color(Color::Muted), ), ) .child(self.over_tier_progress.clone()), ), ) } } impl Component for UserSpending { fn scope() -> ComponentScope { ComponentScope::None } fn preview(_window: &mut Window, cx: &mut App) -> Option { let new_user = cx.new(|cx| UserSpending::new(0, 2000, 0, 2000, cx)); let free_capped = cx.new(|cx| UserSpending::new(2000, 2000, 0, 2000, cx)); let free_near_capped = cx.new(|cx| UserSpending::new(1800, 2000, 0, 2000, cx)); let over_near_capped = cx.new(|cx| UserSpending::new(2000, 2000, 1800, 2000, cx)); let over_capped = cx.new(|cx| UserSpending::new(1000, 2000, 2000, 2000, cx)); Some( v_flex() .gap_6() .p_4() .children(vec![example_group(vec![ single_example( "New User", div().size_full().child(new_user.clone()).into_any_element(), ), single_example( "Free Tier Capped", div() .size_full() .child(free_capped.clone()) .into_any_element(), ), single_example( "Free Tier Near Capped", div() .size_full() .child(free_near_capped.clone()) .into_any_element(), ), single_example( "Over Tier Near Capped", div() .size_full() .child(over_near_capped.clone()) .into_any_element(), ), single_example( "Over Tier Capped", div() .size_full() .child(over_capped.clone()) .into_any_element(), ), ])]) .into_any_element(), ) } }