Skip to content

Commit 4979d86

Browse files
Alek99Alek Petuskey
andauthored
Update pricing (#1163)
* Update pricing * Whitelist * adress pr comments --------- Co-authored-by: Alek Petuskey <[email protected]>
1 parent ffd20b1 commit 4979d86

File tree

3 files changed

+102
-79
lines changed

3 files changed

+102
-79
lines changed

pcweb/pages/pricing/calculator.py

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ def seat_rate(self) -> int:
3030
if self.selected_plan == Tiers.PRO.value:
3131
return 20
3232
elif self.selected_plan == Tiers.TEAM.value:
33-
return 29
33+
return 50
3434

3535
@rx.var(cache=True)
3636
def max_seats(self) -> int:
3737
if self.selected_plan == Tiers.PRO.value:
3838
return 5
3939
elif self.selected_plan == Tiers.TEAM.value:
40-
return 15
40+
return 25
4141

4242
@rx.var(cache=True)
4343
def max_cpu(self) -> int:
@@ -57,12 +57,19 @@ def max_ram(self) -> int:
5757
def change_plan(self, plan: str) -> None:
5858
self.selected_plan = plan
5959
if plan == Tiers.PRO.value:
60-
if self.estimated_cpu_number > 5:
61-
self.estimated_cpu_number = 5
62-
if self.estimated_ram_gb > 10:
63-
self.estimated_ram_gb = 10
64-
if self.estimated_seats > 5:
65-
self.estimated_seats = 5
60+
self.included_cpu = 1
61+
self.included_ram = .5
62+
self.included_seats = 1
63+
# Enforce Pro tier limits
64+
self.estimated_cpu_number = min(self.estimated_cpu_number, 5)
65+
self.estimated_ram_gb = min(self.estimated_ram_gb, 10)
66+
self.estimated_seats = min(self.estimated_seats, 5)
67+
else:
68+
self.included_cpu = 2
69+
self.included_ram = 3
70+
self.included_seats = 5
71+
# Enforce Team tier minimum seats
72+
self.estimated_seats = max(5, self.estimated_seats)
6673

6774

6875
def calculator(text: str, component: rx.Component, total: str) -> rx.Component:
@@ -144,6 +151,21 @@ def stepper(
144151
def pricing_widget() -> rx.Component:
145152
return rx.box(
146153
rx.box(
154+
# Tier
155+
calculator(
156+
"Tier",
157+
rx.box(
158+
rx.segmented_control.root(
159+
rx.segmented_control.item("Pro", value="Pro"),
160+
#rx.segmented_control.item("Team (coming soon)", value="Team"),
161+
on_change=BillingState.change_plan,
162+
default_value="Pro",
163+
width="100%",
164+
),
165+
class_name="flex flex-row pt-2 !w-[8.5rem] !h-[2.25rem] mb-2",
166+
),
167+
"",
168+
),
147169
# Team seats
148170
calculator(
149171
"Members",
@@ -201,39 +223,53 @@ def pricing_widget() -> rx.Component:
201223
rx.center(
202224
rx.flex(
203225
rx.badge(
204-
f"Total: ${calculate_total()}/month",
226+
f"Total: ${calculate_total()}- $20 free credits = ",
227+
rx.text.strong(f"${calculate_total()-20}/mo"),
228+
229+
size='3',
205230
),
206-
size='3',
207-
class_name="mt-6",
231+
class_name="mt-6",
208232
)
209233
),
210234
class_name="flex-1 flex flex-col relative h-full w-full max-w-[25rem] pb-2.5 z-[2]",
211235
)
212236

213237

214238
def calculate_total():
215-
return round(
216-
BillingState.estimated_seats * BillingState.seat_rate
217-
+ BillingState.estimated_ram_gb * (BillingState.mem_rate * MONTH_MINUTES)
218-
+ BillingState.estimated_cpu_number * (BillingState.cpu_rate * MONTH_MINUTES)
219-
- 25
239+
# Base price using rx.cond
240+
base_price = rx.cond(
241+
BillingState.selected_plan == Tiers.PRO.value,
242+
20,
243+
250
244+
)
245+
246+
# Calculate additional seats cost
247+
additional_seats = rx.cond(
248+
BillingState.estimated_seats > 1,
249+
BillingState.estimated_seats - 1,
250+
0
220251
)
252+
seat_cost = additional_seats * BillingState.seat_rate
253+
254+
compute_cost = (
255+
(BillingState.estimated_ram_gb) * (BillingState.mem_rate * MONTH_MINUTES) +
256+
(BillingState.estimated_cpu_number) * (BillingState.cpu_rate * MONTH_MINUTES)
257+
)
258+
259+
total = base_price + seat_cost + compute_cost
260+
return round(total)
221261

222262

223263
def header() -> rx.Component:
224264
return rx.box(
225265
rx.el.h3(
226-
"Calculate costs.",
266+
"Cost Estimate",
227267
class_name="text-slate-12 text-3xl font-semibold text-center",
228268
id="calculator-header",
229269
),
230270
rx.el.p(
231-
"Simply usage based pricing.",
232-
class_name="text-slate-9 text-3xl font-semibold text-center",
233-
),
234-
rx.el.p(
235-
"We subtract the hobby tier free CPU and RAM from your usage.",
236-
class_name="text-slate-9 text-md font-medium text-center mt-2",
271+
"Get a price estimate for your organization.",
272+
class_name="text-slate-9 text-2xl font-semibold text-center",
237273
),
238274
class_name="flex items-center mb-5 justify-between text-slate-11 flex-col pt-[5rem] mx-auto w-full",
239275
)

pcweb/pages/pricing/plan_cards.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ def card(
150150
rx.el.li(
151151
rx.icon(feature[0], class_name="!text-slate-9", size=16),
152152
feature[1],
153+
rx.tooltip(
154+
rx.icon("info", class_name="!text-slate-9", size=12),
155+
content=feature[2],
156+
) if len(feature) == 3 else "",
153157
class_name="text-sm font-medium text-slate-11 flex items-center gap-3",
154158
)
155159
for feature in features
@@ -222,12 +226,12 @@ def plan_cards() -> rx.Component:
222226
return rx.box(
223227
card(
224228
"Hobby",
225-
"Everything you need to get started with Reflex.",
229+
"Everything you need to get started.",
226230
[
227-
("code", "Open Source Framework"),
228231
("heart-handshake", "Community support"),
229-
("user", "1 team member"),
230-
("app-window", "1 Deployed app"),
232+
("app-window", "1 app included", "Free apps shutdown after 1 hour of inactivity, and are restarted on demand (usually within a few seconds)."),
233+
("clock", "1 day log retention"),
234+
("code", "Open Source Framework"),
231235
("file-code", "Starter Templates"),
232236
],
233237
"Start building for free",
@@ -236,48 +240,46 @@ def plan_cards() -> rx.Component:
236240
popular_card(
237241
"Pro",
238242
"For professional projects and startups.",
239-
[
240-
("server", "Larger machine sizes"),
241-
("users", "Up to 5 team members"),
242-
("app-window", "Up to 5 Deployed apps"),
243+
[
244+
("heart-handshake", "Community support"),
245+
("users", "1 seat incl. (additional seats at $20/mo)"),
246+
("credit-card", "$20 / month free compute credits"),
247+
("app-window", "Up to 5 apps"),
248+
("server", "Customizable machine sizes"),
243249
("clock", "30 days log retention"),
244250
("globe", "Multi-region"),
245251
("brush", "Custom domains"),
246-
("wand", "AI Tools for Building and Debugging"),
247-
("circle-plus", "Everything in Hobby"),
248252
],
249253
"Start with Pro plan",
250-
price="$20/mo + usage",
254+
price="$20/mo + compute",
251255
),
252256
card(
253257
"Team",
254258
"For teams looking to scale their applications.",
255259
[
256-
("mail", "Email support"),
257-
("users", "Up to 25 team members"),
258-
("app-window", "Unlimited Apps"),
260+
("mail", "Email/Slack support"),
261+
("users", "5 seat incl. (additional seats available)"),
262+
("credit-card", "Monthly free compute credits"),
263+
("app-window", "Unlimited apps"),
264+
("git-branch", "Create multiple projects"),
259265
("signal", "Full Website Analytics"),
260266
("lock-keyhole", "One Click Auth"),
261-
("git-branch", "Dev, Stage & Prod Envs"),
262-
("database", "DB Editor UI and Migration Tool"),
263-
("test-tube", "Built-in Testing Framework"),
264267
("circle-plus", "Everything in Pro"),
265268
],
266269
"Contact sales",
267270
),
268271
card(
269272
"Enterprise",
270-
"Get our priority support and a plan tailored to your needs.",
273+
"Get a plan tailored to your business needs.",
271274
[
272275
("headset", "Priority Engineering Support"),
273-
("user-round-plus", "White Glove Onboarding"),
274-
("users", "Unlimited team members"),
276+
("users", "Customized seat amount"),
277+
("user-round-plus", "Personalized integration help"),
275278
("hard-drive", "On Premise Deployment"),
276-
("signal", "Full Analytics Dashboard"),
277279
("clock", "Unlimited log retention"),
278280
("activity", "Error Monitoring and Observability"),
279281
("git-pull-request", "Influence Reflex Roadmap"),
280-
("shield-check", "Custom SSO"),
282+
("shield-check", "Audit logs, SSO, SOC2 Reports"),
281283
("circle-plus", "Everything in Team"),
282284
],
283285
"Contact sales",

pcweb/pages/pricing/table.py

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
STYLES = {
77
"cell": "text-slate-12 font-medium text-sm whitespace-nowrap",
88
"header_cell": "text-slate-12 font-semibold text-lg",
9+
"header_cell_sub": "text-slate-11 font-semibold text-md",
910
"feature_cell": "text-slate-9 font-medium text-sm whitespace-nowrap",
1011
"button_base": "!text-sm !font-semibold w-full text-nowrap",
1112
}
@@ -32,27 +33,16 @@
3233
}
3334
"""
3435

35-
# Data configuration
36-
USERS_SECTION = [
37-
("Per Seat Price", "Free", "$20/mo/user", "Contact Sales", "Contact Sales"),
38-
("User Limit", "1", "5", "25", "Unlimited"),
39-
]
40-
4136
FRAMEWORK_SECTION = [
4237
("Open Source Framework", True, True, True, True),
43-
("Starter Templates", True, True, True, True),
44-
("Enterprise Templates", False, False, True, True),
38+
("Templates", True, True, True, True),
4539
("One Click Auth", False, False, True, True),
4640
("Embed Reflex Apps", False, False, True, True),
4741
("Built-in Testing", False, False, True, True),
4842
]
4943

50-
THEME_SECTION = [("Theming", "Builtin Themes", "Builtin Themes", "Custom Themes", "Custom Themes")]
51-
5244
REFLEX_AI_SECTION = [
53-
("Flexgen Website Builder", "5/day", "20/day", "100/day", "Custom"),
54-
("Full-Stack AI Agent", "5/day", "50/day", "250/day", "Custom"),
55-
("AI Assistant / Debugger", "5/day", "50/day", "250/day", "Custom"),
45+
("Number of Generations", "5/month", "100/month/seat", "250/month/seat", "Custom"),
5646
]
5747

5848
DATABASE_SECTION = [
@@ -64,13 +54,13 @@
6454
HOSTING_TEXT_SECTION = [
6555
("Compute Limits", "1 CPU, .5GB", "5 CPU, 10GB", "Custom", "Custom"),
6656
("Regions", "Single", "Multiple", "Multiple", "Multiple"),
67-
("Custom Domains", "None", "1", "5", "Unlimited"),
6857
("Build logs", "7 day", "30 days", "90 days", "Custom"),
6958
("Runtime logs", "1 day", "7 days", "30 days", "Custom"),
7059
]
7160

7261
HOSTING_BOOLEAN_SECTION = [
7362
("CLI Deployments", True, True, True, True),
63+
("Custom Domains", False, True, True, True),
7464
("Automatic CI / CD Deploy (Github)", False, False, True, True),
7565
("Secrets", True, True, True, True),
7666
("Secret Manager", False, False, True, True),
@@ -90,11 +80,11 @@
9080
("Rich Permissions Control", False, False, True, True),
9181
("Connect to Analytics Vendors", False, False, True, True),
9282
("Audit Logs", False, False, False, True),
93-
("Custom SSO", False, False, False, True),
83+
("SSO", False, False, False, True),
9484
]
9585

9686
SUPPORT_TEXT_SECTION = [
97-
("Support", "Community", "Community", "Email Support", "Dedicated Support")
87+
("Support", "Community", "Community", "Email/Slack", "Dedicated Support")
9888
]
9989

10090
SUPPORT_BOOLEAN_SECTION = [
@@ -154,15 +144,18 @@ def create_table_row(cells: list) -> rx.Component:
154144
row_cells = [create_table_cell(cell) for cell in cells]
155145
return rx.table.row(
156146
*row_cells,
157-
class_name="w-full [&>*:not(:first-child)]:text-center bg-slate-1 z-[2] !h-[56px]",
147+
class_name="w-full [&>*:not(:first-child)]:text-center bg-slate-1 z-[2] !h-[50px]",
158148
)
159149

160150

161-
def create_table_row_header(cells: list, coming_soon: bool = False) -> rx.Component:
151+
def create_table_row_header(name: list, coming_soon: bool = False) -> rx.Component:
162152
return rx.table.row(
163153
*[
164-
rx.table.column_header_cell(cell, rx.badge("coming soon", margin_left="0.5rem"), class_name=STYLES["header_cell"]) if cell and coming_soon else rx.table.column_header_cell(cell, class_name=STYLES["header_cell"])
165-
for cell in cells
154+
rx.table.column_header_cell(name, rx.badge("coming soon", margin_left="0.5rem"), class_name=STYLES["header_cell"]) if coming_soon else rx.table.column_header_cell(name, class_name=STYLES["header_cell"]),
155+
rx.table.column_header_cell("Hobby", class_name=STYLES["header_cell_sub"]),
156+
rx.table.column_header_cell("Pro", class_name=STYLES["header_cell_sub"]),
157+
rx.table.column_header_cell("Team", class_name=STYLES["header_cell_sub"]),
158+
rx.table.column_header_cell("Enterprise", class_name=STYLES["header_cell_sub"])
166159
],
167160
class_name="w-full [&>*:not(:first-child)]:text-center bg-slate-2 border border-slate-3 rounded-2xl z-[6] !h-[3.625rem] relative",
168161
padding_x="5rem !important",
@@ -223,17 +216,10 @@ def table_body_hosting() -> rx.Component:
223216
return rx.table.root(
224217
rx.el.style(TABLE_STYLE),
225218
rx.table.header(
226-
create_table_row_header(["Price", "Hobby", "Pro", "Team", "Enterprise"]),
219+
create_table_row_header("Hosting"),
227220
glow(),
228221
class_name="relative",
229222
),
230-
create_table_body(
231-
*[create_table_row(row) for row in USERS_SECTION],
232-
),
233-
rx.table.header(
234-
create_table_row_header(["Hosting", "", "", ""]),
235-
class_name="relative",
236-
),
237223
create_table_body(
238224
*[create_table_row(row) for row in HOSTING_TEXT_SECTION],
239225
*[
@@ -242,7 +228,7 @@ def table_body_hosting() -> rx.Component:
242228
],
243229
),
244230
rx.table.header(
245-
create_table_row_header(["Security", "", "", "", ""]),
231+
create_table_row_header("Security"),
246232
class_name="relative",
247233
),
248234
create_table_body(
@@ -252,7 +238,7 @@ def table_body_hosting() -> rx.Component:
252238
],
253239
),
254240
rx.table.header(
255-
create_table_row_header(["Support", "", "", "", ""]),
241+
create_table_row_header("Support"),
256242
class_name="relative",
257243
),
258244
create_table_body(
@@ -270,18 +256,17 @@ def table_body_oss() -> rx.Component:
270256
return rx.table.root(
271257
rx.el.style(TABLE_STYLE),
272258
rx.table.header(
273-
create_table_row_header(["Framework","Hobby", "Pro", "Team", "Enterprise"]),
259+
create_table_row_header("Framework"),
274260
class_name="relative",
275261
),
276262
create_table_body(
277263
*[
278264
create_checkmark_row(feature, checks)
279265
for feature, *checks in FRAMEWORK_SECTION
280266
],
281-
*[create_table_row(row) for row in THEME_SECTION],
282267
),
283268
rx.table.header(
284-
create_table_row_header(["Database", "", "", ""]),
269+
create_table_row_header("Database"),
285270
class_name="relative",
286271
),
287272
create_table_body(
@@ -291,7 +276,7 @@ def table_body_oss() -> rx.Component:
291276
],
292277
),
293278
rx.table.header(
294-
create_table_row_header(["AI", "", "", ""], coming_soon=True),
279+
create_table_row_header("AI", coming_soon=True),
295280
class_name="relative",
296281
),
297282
create_table_body(

0 commit comments

Comments
 (0)