Skip to content

Commit 61a8f33

Browse files
carlosabadiaLineIndentAlek99
authored
pricing page updates (#1643)
* init * more updates * typo * updates with credits * update * yearly * just pro * just pro * update * updates * fix custom * hide yearly * preview btn change * .png -> .webp * Update pricing calculator with new credit calculation logic and UI improvements --------- Co-authored-by: Ahmad Hakim <[email protected]> Co-authored-by: Alek Petuskey <[email protected]>
1 parent 2ca0a6f commit 61a8f33

File tree

17 files changed

+1141
-994
lines changed

17 files changed

+1141
-994
lines changed
18.1 KB
Loading

pcweb/components/docpage/navbar/buttons/discord.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def discord() -> rx.Component:
77
return rx.link(
88
get_icon(icon="discord_navbar", class_name="shrink-0 !text-slate-9"),
99
custom_attrs={"aria-label": "Discord link"},
10-
class_name="hover:bg-slate-3 size-8 text-slate-9 flex justify-center items-center rounded-[10px] border border-solid border-slate-5 bg-slate-1 transition-bg cursor-pointer shadow-large py-0.5 px-3 hover:!text-slate-9",
10+
class_name="hover:bg-slate-3 size-8 text-slate-9 flex justify-center items-center rounded-[10px] border border-solid border-slate-5 bg-slate-1 transition-bg cursor-pointer py-0.5 px-3 hover:!text-slate-9",
1111
underline="none",
1212
href=DISCORD_URL,
1313
)

pcweb/components/docpage/navbar/buttons/github.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def github() -> rx.Component:
1212
GithubStarState.stars_short,
1313
class_name="font-small",
1414
),
15-
class_name="text-slate-9 flex-row gap-2 hover:bg-slate-3 flex justify-center rounded-[10px] border border-slate-5 bg-slate-1 transition-bg cursor-pointer shadow-large py-0.5 px-3 items-center h-8",
15+
class_name="text-slate-9 flex-row gap-2 hover:bg-slate-3 flex justify-center rounded-[10px] border border-slate-5 bg-slate-1 transition-bg cursor-pointer py-0.5 px-3 items-center h-8",
1616
),
1717
href=GITHUB_URL,
1818
underline="none",

pcweb/components/docpage/navbar/typesense.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ def keyboard_shortcut_script() -> rx.Component:
246246
def search_trigger() -> rx.Component:
247247
"""Render the search trigger button."""
248248
return rx.box(
249-
rx.icon(
250-
"search",
249+
ui.icon(
250+
"Search01Icon",
251251
class_name="md:absolute md:left-2 md:top-1/2 md:transform md:-translate-y-1/2 text-md w-4 h-4 flex-shrink-0 !text-slate-9",
252252
),
253253
rx.text(
@@ -257,7 +257,7 @@ def search_trigger() -> rx.Component:
257257
rx.el.input(
258258
placeholder="Search",
259259
read_only=True,
260-
class_name="bg-transparent border-none outline-none focus:outline-none pl-4 cursor-pointer hidden md:block",
260+
class_name="bg-transparent border-none outline-none focus:outline-none pl-4 cursor-pointer hidden md:block font-medium",
261261
),
262262
class_name="py-[6px] md:px-[12px] w-8 md:w-full hover:bg-slate-3 cursor-pointer flex items-center justify-center h-8 border border-slate-5 rounded-[10px] bg-slate-1 transition-bg relative",
263263
)

pcweb/components/number_flow.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import reflex as rx
2+
3+
4+
class NumberFlow(rx.Component):
5+
library = "@number-flow/react"
6+
7+
tag = "NumberFlow"
8+
9+
is_default = True
10+
11+
# The numeric value to display and animate
12+
# NumberFlow will automatically transition when this prop changes
13+
value: rx.Var[int | str | float]
14+
15+
# The locale(s) for number formatting (e.g., "en-US", "de-DE")
16+
# Accepts Intl.LocalesArgument format
17+
# Default: "en-US"
18+
locales: rx.Var[str] = rx.Var.create("en-US")
19+
20+
# Custom prefix string to display before the number
21+
# Example: "$" for currency display
22+
prefix: rx.Var[str]
23+
24+
# Custom suffix string to display after the number
25+
# Example: "/mo" for monthly pricing, "%" for percentages
26+
suffix: rx.Var[str]
27+
28+
# If true, NumberFlow's transitions are isolated from other layout changes
29+
# that may occur in the same update. Has no effect when inside NumberFlowGroup.
30+
# Default: false
31+
isolate: rx.Var[bool]
32+
33+
# Controls whether animations are enabled
34+
# Can be set to false to disable all animations and finish current ones
35+
# Useful for scenarios like input fields where you want instant updates
36+
# Default: true
37+
animated: rx.Var[bool]
38+
39+
# If true, applies CSS will-change properties to relevant elements
40+
# Useful if your number changes frequently or you experience unwanted repositioning
41+
# Note: Excessive use can result in excessive memory usage
42+
# Default: false
43+
will_change: rx.Var[bool]
44+
45+
# Controls the direction of digit animations
46+
# "+1": digits always go up
47+
# "0": each digit goes up if increasing, down if decreasing (no overall trend)
48+
# "-1": digits always go down
49+
# Can also be a function: (oldValue: number, value: number) => number
50+
# Default: "+1" (which corresponds to Math.sign(value - oldValue))
51+
trend: rx.Var[str] = rx.Var.create("+1")
52+
53+
54+
number_flow = NumberFlow.create

pcweb/constants.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,13 @@
9999
POSTHOG_API_KEY = os.getenv("POSTHOG_API_KEY")
100100

101101
SLACK_DEMO_WEBHOOK_URL: str = os.environ.get("SLACK_DEMO_WEBHOOK_URL")
102+
103+
# Pricing
104+
PRO_TIERS_TABLE = {
105+
"Pro 50": {"price": 50, "credits": 1000},
106+
"Pro 100": {"price": 100, "credits": 2000},
107+
"Pro 250": {"price": 250, "credits": 5000},
108+
"Pro 500": {"price": 500, "credits": 10000},
109+
"Pro 750": {"price": 750, "credits": 15000},
110+
"Pro 1000": {"price": 1000, "credits": 20000},
111+
}

pcweb/pages/pricing/calculator.py

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import reflex as rx
2-
2+
from reflex.experimental.client_state import ClientStateVar
33
from pcweb.components.tabs import tabs
4+
import reflex_ui as ui
5+
6+
collapsed_calculator_cs = ClientStateVar.create("collapsed_calculator", default=True)
7+
8+
CREDITS_PER_HOUR_CPU = 0.2
9+
CREDITS_PER_HOUR_GB = 0.5
410

511
COMPUTE_TABLE = {
6-
"c1m.5": {"vcpu": 1, "ram": 0.5, "pph": 0.046},
7-
"c1m1": {"vcpu": 1, "ram": 1, "pph": 0.083},
8-
"c1m2": {"vcpu": 1, "ram": 2, "pph": 0.157},
9-
"c2m2": {"vcpu": 2, "ram": 2, "pph": 0.166},
10-
"c2m4": {"vcpu": 2, "ram": 4, "pph": 0.312},
11-
"c4m4": {"vcpu": 4, "ram": 4, "pph": 0.332},
12-
"c4m8": {"vcpu": 4, "ram": 8, "pph": 0.625},
12+
"c1m.5": {"vcpu": 1, "ram": 0.5},
13+
"c1m1": {"vcpu": 1, "ram": 1},
14+
"c1m2": {"vcpu": 1, "ram": 2},
15+
"c2m2": {"vcpu": 2, "ram": 2},
16+
"c2m4": {"vcpu": 2, "ram": 4},
17+
"c4m4": {"vcpu": 4, "ram": 4},
18+
"c4m8": {"vcpu": 4, "ram": 8},
1319
}
1420

1521

@@ -18,20 +24,30 @@ def table_header(cost_text: str) -> rx.Component:
1824
rx.text("Machine", class_name="text-sm font-semibold text-slate-12"),
1925
rx.text("vCPU", class_name="text-sm font-semibold text-slate-12"),
2026
rx.text("GB RAM", class_name="text-sm font-semibold text-slate-12"),
21-
rx.text(cost_text, class_name="text-sm font-semibold text-slate-12 text-end"),
27+
rx.el.div(
28+
ui.icon("StarCircleIcon", class_name="text-slate-9 size-4"),
29+
rx.text(
30+
cost_text, class_name="text-sm font-semibold text-slate-12 text-end"
31+
),
32+
class_name="flex flex-row gap-1 items-center justify-end",
33+
),
2234
class_name="grid grid-cols-4 gap-4 px-6 py-3 border-b border-slate-4",
2335
)
2436

2537

26-
def table_row(name: str, cpu: str, ram: str, cost: str) -> rx.Component:
38+
def table_row(name: str, cpu: str, ram: str, cost: str | rx.Component) -> rx.Component:
2739
return rx.box(
2840
rx.box(
2941
name,
3042
class_name="px-2 w-fit text-slate-12 border-slate-6 h-5 rounded-md border justify-start items-center gap-0.5 inline-flex bg-slate-1 text-sm font-medium shrink-0",
3143
),
3244
rx.text(cpu, class_name="font-medium text-sm text-slate-9"),
3345
rx.text(ram, class_name="font-medium text-sm text-slate-9"),
34-
rx.text(cost, class_name="font-medium text-sm text-slate-9 text-end"),
46+
(
47+
rx.text(cost, class_name="font-medium text-sm text-slate-9 text-end")
48+
if isinstance(cost, str)
49+
else cost
50+
),
3551
class_name="grid grid-cols-4 gap-4 px-6 py-2 border-b border-slate-4 hover:bg-slate-2",
3652
)
3753

@@ -48,7 +64,6 @@ def learn_more():
4864
" only when your app is being used.",
4965
class_name="text-sm font-medium text-slate-9",
5066
),
51-
class_name="px-6 py-4 border-b border-slate-4 hover:bg-slate-2",
5267
)
5368

5469

@@ -60,11 +75,16 @@ def min_table(cost_text: str, description: bool = False) -> rx.Component:
6075
name,
6176
str(specs["vcpu"]),
6277
str(specs["ram"]),
63-
f"${specs['pph'] / 60:.6f}",
78+
rx.el.span(
79+
rx.el.span(
80+
f"{(specs['vcpu'] * CREDITS_PER_HOUR_CPU + specs['ram'] * CREDITS_PER_HOUR_GB) / 60:.3f}",
81+
class_name="font-medium text-sm text-slate-9",
82+
),
83+
class_name="flex flex-row gap-1 items-center justify-end",
84+
),
6485
)
6586
for name, specs in COMPUTE_TABLE.items()
6687
],
67-
learn_more() if description else rx.fragment(),
6888
class_name="w-full",
6989
)
7090

@@ -77,11 +97,16 @@ def hour_table(cost_text: str, description: bool = False) -> rx.Component:
7797
name,
7898
str(specs["vcpu"]),
7999
str(specs["ram"]),
80-
f"${specs['pph']:.3f}",
100+
rx.el.span(
101+
rx.el.span(
102+
f"{specs['vcpu'] * CREDITS_PER_HOUR_CPU + specs['ram'] * CREDITS_PER_HOUR_GB:.3f}",
103+
class_name="font-medium text-sm text-slate-9",
104+
),
105+
class_name="flex flex-row gap-1 items-center justify-end",
106+
),
81107
)
82108
for name, specs in COMPUTE_TABLE.items()
83109
],
84-
learn_more() if description else rx.fragment(),
85110
class_name="w-full",
86111
)
87112

@@ -94,7 +119,13 @@ def month_table(cost_text: str) -> rx.Component:
94119
name,
95120
str(specs["vcpu"]),
96121
str(specs["ram"]),
97-
f"${specs['price_per_min'] * 60 * 24 * 30:.2f}",
122+
rx.el.span(
123+
rx.el.span(
124+
f"{specs['vcpu'] * CREDITS_PER_HOUR_CPU + specs['ram'] * CREDITS_PER_HOUR_GB:.3f}",
125+
class_name="font-medium text-sm text-slate-9",
126+
),
127+
class_name="flex flex-row gap-1 items-center justify-end",
128+
),
98129
)
99130
for name, specs in COMPUTE_TABLE.items()
100131
],
@@ -106,17 +137,7 @@ def compute_table() -> rx.Component:
106137
return rx.box(
107138
tabs.root(
108139
rx.box(
109-
rx.box(
110-
rx.el.h3(
111-
"Compute Pricing",
112-
class_name="text-slate-12 text-3xl font-semibold",
113-
),
114-
rx.el.p(
115-
"Only pay when your app is being used, nothing more.",
116-
class_name="text-slate-9 text-lg font-semibold text-center lg:text-start text-balance",
117-
),
118-
class_name="flex flex-col gap-1 justify-center items-center lg:items-start lg:justify-start",
119-
),
140+
learn_more(),
120141
tabs.list(
121142
tabs.tab(
122143
"Per min",
@@ -131,16 +152,17 @@ def compute_table() -> rx.Component:
131152
class_name="flex flex-col lg:flex-row gap-4 items-center justify-center lg:justify-between p-6 border-b border-slate-4",
132153
),
133154
tabs.panel(
134-
min_table("Cost / min", description=True),
155+
min_table("Credits / min", description=True),
135156
value="min",
136157
),
137158
tabs.panel(
138-
hour_table("Cost / hour", description=True),
159+
hour_table("Credits / hour", description=True),
139160
value="hour",
140161
),
141162
default_value="min",
142163
),
143-
class_name="flex flex-col w-full mt-5 py-24",
164+
class_name="flex flex-col w-full relative data-[collapsed=true]:max-h-[11.5rem] data-[collapsed=true]:overflow-hidden transition-all",
165+
data_collapsed=collapsed_calculator_cs.value,
144166
)
145167

146168

@@ -171,11 +193,11 @@ def compute_table_base() -> rx.Component:
171193
class_name="flex flex-row gap-2 items-center justify-end pb-6 border-b border-slate-4",
172194
),
173195
tabs.panel(
174-
min_table("Cost / min"),
196+
min_table("Credits / min"),
175197
value="min",
176198
),
177199
tabs.panel(
178-
hour_table("Cost / hour"),
200+
hour_table("Credits / hour"),
179201
value="hour",
180202
),
181203
default_value="min",
@@ -187,5 +209,35 @@ def compute_table_base() -> rx.Component:
187209
def calculator_section() -> rx.Component:
188210
return rx.el.section(
189211
compute_table(),
190-
class_name="flex flex-col w-full max-w-[64.19rem] 2xl:border-x border-slate-4 2xl:border-t 2xl:border-b pb-[6rem] justify-center items-center",
212+
rx.el.div(
213+
rx.cond(
214+
collapsed_calculator_cs.value,
215+
rx.el.div(
216+
class_name="from-slate-1 to-transparent absolute z-10 transition-opacity pointer-events-none opacity-100 bg-linear-to-t -top-27 left-0 w-full h-29",
217+
),
218+
),
219+
ui.button(
220+
ui.icon(
221+
"ArrowDown01Icon",
222+
size=16,
223+
class_name=(
224+
"transition-all",
225+
rx.cond(collapsed_calculator_cs.value, "", "rotate-180"),
226+
),
227+
),
228+
rx.cond(
229+
collapsed_calculator_cs.value,
230+
"Expand Compute Pricing",
231+
"Collapse Compute Pricing",
232+
),
233+
on_click=collapsed_calculator_cs.set_value(
234+
~collapsed_calculator_cs.value
235+
),
236+
size="sm",
237+
variant="outline",
238+
class_name="w-full",
239+
),
240+
class_name="w-full p-2 relative",
241+
),
242+
class_name="flex flex-col w-full max-w-[64.19rem] 2xl:border-x border-slate-4 2xl:border-b pb-[6rem] justify-center items-center",
191243
)

pcweb/pages/pricing/faq.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,20 @@ def header() -> rx.Component:
2020
"Check FAQ",
2121
class_name="text-slate-9 text-3xl font-semibold",
2222
),
23-
class_name="flex items-center justify-between text-slate-11 flex-col pt-[5rem] xl:border-x border-slate-4",
23+
class_name="flex items-center justify-between text-slate-11 flex-col pt-[5rem]",
2424
)
2525

2626

2727
def sales_button() -> rx.Component:
28-
return lemcal_dialog(
29-
rx.fragment(
30-
glow(),
31-
button(
32-
"Need more help? Contact sales",
33-
variant="secondary",
34-
class_name="!text-slate-11 !font-semibold !text-sm",
28+
return rx.el.div(
29+
lemcal_dialog(
30+
rx.el.div(
31+
glow(),
32+
button(
33+
"Need more help? Contact sales",
34+
variant="secondary",
35+
class_name="!text-slate-11 !font-semibold !text-sm w-fit",
36+
),
3537
),
3638
),
3739
class_name="self-center relative",
@@ -97,6 +99,10 @@ def accordion_text(text: str) -> rx.Component:
9799
"What happens when I upgrade?",
98100
"When you upgrade your plan, you'll immediately get access to the new features and increased resource limits. Your app will continue running without interruption.",
99101
),
102+
(
103+
"What are credits?",
104+
"Credits can be used for both hosting and building. In hosting, credits are used for compute resources. In building, credits are consumed per message sent.",
105+
),
100106
(
101107
"What happens if I cancel the plan?",
102108
"If you cancel, you'll maintain access until the end of your current billing period. After that, your app will be downgraded to the free tier limits.",

0 commit comments

Comments
 (0)