1use gpui::{Entity, Render};
2use ui::{ProgressBar, prelude::*};
3
4#[derive(RegisterComponent)]
5pub struct UserSpending {
6 free_tier_current: u32,
7 free_tier_cap: u32,
8 over_tier_current: u32,
9 over_tier_cap: u32,
10 free_tier_progress: Entity<ProgressBar>,
11 over_tier_progress: Entity<ProgressBar>,
12}
13
14impl UserSpending {
15 pub fn new(
16 free_tier_current: u32,
17 free_tier_cap: u32,
18 over_tier_current: u32,
19 over_tier_cap: u32,
20 cx: &mut App,
21 ) -> Self {
22 let free_tier_capped = free_tier_current == free_tier_cap;
23 let free_tier_near_capped =
24 free_tier_current as f32 / 100.0 >= free_tier_cap as f32 / 100.0 * 0.9;
25 let over_tier_capped = over_tier_current == over_tier_cap;
26 let over_tier_near_capped =
27 over_tier_current as f32 / 100.0 >= over_tier_cap as f32 / 100.0 * 0.9;
28
29 let free_tier_progress = cx.new(|cx| {
30 ProgressBar::new(
31 "free_tier",
32 free_tier_current as f32,
33 free_tier_cap as f32,
34 cx,
35 )
36 });
37 let over_tier_progress = cx.new(|cx| {
38 ProgressBar::new(
39 "over_tier",
40 over_tier_current as f32,
41 over_tier_cap as f32,
42 cx,
43 )
44 });
45
46 if free_tier_capped {
47 free_tier_progress.update(cx, |progress_bar, cx| {
48 progress_bar.fg_color(cx.theme().status().error);
49 });
50 } else if free_tier_near_capped {
51 free_tier_progress.update(cx, |progress_bar, cx| {
52 progress_bar.fg_color(cx.theme().status().warning);
53 });
54 }
55
56 if over_tier_capped {
57 over_tier_progress.update(cx, |progress_bar, cx| {
58 progress_bar.fg_color(cx.theme().status().error);
59 });
60 } else if over_tier_near_capped {
61 over_tier_progress.update(cx, |progress_bar, cx| {
62 progress_bar.fg_color(cx.theme().status().warning);
63 });
64 }
65
66 Self {
67 free_tier_current,
68 free_tier_cap,
69 over_tier_current,
70 over_tier_cap,
71 free_tier_progress,
72 over_tier_progress,
73 }
74 }
75}
76
77impl Render for UserSpending {
78 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
79 let formatted_free_tier = format!(
80 "${} / ${}",
81 self.free_tier_current as f32 / 100.0,
82 self.free_tier_cap as f32 / 100.0
83 );
84 let formatted_over_tier = format!(
85 "${} / ${}",
86 self.over_tier_current as f32 / 100.0,
87 self.over_tier_cap as f32 / 100.0
88 );
89
90 v_group()
91 .elevation_2(cx)
92 .py_1p5()
93 .px_2p5()
94 .w(px(360.))
95 .child(
96 v_flex()
97 .child(
98 v_flex()
99 .p_1p5()
100 .gap_0p5()
101 .child(
102 h_flex()
103 .justify_between()
104 .child(Label::new("Free Tier Usage").size(LabelSize::Small))
105 .child(
106 Label::new(formatted_free_tier)
107 .size(LabelSize::Small)
108 .color(Color::Muted),
109 ),
110 )
111 .child(self.free_tier_progress.clone()),
112 )
113 .child(
114 v_flex()
115 .p_1p5()
116 .gap_0p5()
117 .child(
118 h_flex()
119 .justify_between()
120 .child(Label::new("Current Spending").size(LabelSize::Small))
121 .child(
122 Label::new(formatted_over_tier)
123 .size(LabelSize::Small)
124 .color(Color::Muted),
125 ),
126 )
127 .child(self.over_tier_progress.clone()),
128 ),
129 )
130 }
131}
132
133impl Component for UserSpending {
134 fn scope() -> ComponentScope {
135 ComponentScope::None
136 }
137
138 fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
139 let new_user = cx.new(|cx| UserSpending::new(0, 2000, 0, 2000, cx));
140 let free_capped = cx.new(|cx| UserSpending::new(2000, 2000, 0, 2000, cx));
141 let free_near_capped = cx.new(|cx| UserSpending::new(1800, 2000, 0, 2000, cx));
142 let over_near_capped = cx.new(|cx| UserSpending::new(2000, 2000, 1800, 2000, cx));
143 let over_capped = cx.new(|cx| UserSpending::new(1000, 2000, 2000, 2000, cx));
144
145 Some(
146 v_flex()
147 .gap_6()
148 .p_4()
149 .children(vec![example_group(vec![
150 single_example(
151 "New User",
152 div().size_full().child(new_user.clone()).into_any_element(),
153 ),
154 single_example(
155 "Free Tier Capped",
156 div()
157 .size_full()
158 .child(free_capped.clone())
159 .into_any_element(),
160 ),
161 single_example(
162 "Free Tier Near Capped",
163 div()
164 .size_full()
165 .child(free_near_capped.clone())
166 .into_any_element(),
167 ),
168 single_example(
169 "Over Tier Near Capped",
170 div()
171 .size_full()
172 .child(over_near_capped.clone())
173 .into_any_element(),
174 ),
175 single_example(
176 "Over Tier Capped",
177 div()
178 .size_full()
179 .child(over_capped.clone())
180 .into_any_element(),
181 ),
182 ])])
183 .into_any_element(),
184 )
185 }
186}