Skip to content

Commit c542e6b

Browse files
authored
add posthog event metric (#1434)
* add posthog event metric * word * fix email
1 parent 87061a4 commit c542e6b

File tree

5 files changed

+258
-47
lines changed

5 files changed

+258
-47
lines changed

pcweb/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,6 @@
7373
MONTHLY_USERS = 10000
7474
DISCORD_USERS = 7000
7575
CONTRIBUTORS = 170
76+
77+
# Posthog
78+
POSTHOG_API_KEY = os.getenv("POSTHOG_API_KEY")

pcweb/pages/pricing/header.py

Lines changed: 166 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import urllib.parse
88
from datetime import datetime
99
from reflex.event import EventType
10-
10+
from pcweb.telemetry.postog_metrics import DemoEvent, send_data_to_posthog
1111

1212
SelectVariant = Literal["primary", "secondary", "outline", "transparent"]
1313
SelectSize = Literal["sm", "md", "lg"]
@@ -29,6 +29,7 @@
2929
"lg": "text-sm px-3 h-10 rounded-xl gap-3",
3030
}
3131

32+
3233
def select_item(
3334
content: tuple[str | rx.Component, EventType[()]],
3435
is_selected: bool = False,
@@ -62,6 +63,7 @@ def select_item(
6263
)
6364
# return rx.el.button(text, **common_props)
6465

66+
6567
def select(
6668
content: rx.Component,
6769
variant: SelectVariant = "primary",
@@ -104,13 +106,14 @@ def select(
104106
**props,
105107
)
106108

109+
107110
class QuoteFormState(rx.State):
108111
"""State management for the quote form."""
112+
109113
num_employees: str = "500+"
110114
referral_source: str = "Google Search"
111115
banned_email: bool = False
112116

113-
114117
def set_select_value(self, field: str, value: str):
115118
"""Update the selected value for a given field."""
116119
setattr(self, field, value)
@@ -119,27 +122,27 @@ def set_select_value(self, field: str, value: str):
119122
def submit(self, form_data: dict[str, Any]):
120123
# Email domain validation
121124
banned_domains = [
122-
'gmail.com',
123-
'outlook.com',
124-
'hotmail.com',
125-
'yahoo.com',
126-
'icloud.com',
127-
'aol.com',
128-
'protonmail.com',
129-
'proton.me',
130-
'mail.com',
131-
'yandex.com',
132-
'zoho.com',
133-
'live.com',
134-
'msn.com',
135-
'me.com',
136-
'mac.com',
137-
'googlemail.com',
138-
'yahoo.co.uk',
139-
'yahoo.ca',
140-
'yahoo.co.in',
141-
'outlook.co.uk',
142-
'hotmail.co.uk'
125+
"gmail.com",
126+
"outlook.com",
127+
"hotmail.com",
128+
"yahoo.com",
129+
"icloud.com",
130+
"aol.com",
131+
"protonmail.com",
132+
"proton.me",
133+
"mail.com",
134+
"yandex.com",
135+
"zoho.com",
136+
"live.com",
137+
"msn.com",
138+
"me.com",
139+
"mac.com",
140+
"googlemail.com",
141+
"yahoo.co.uk",
142+
"yahoo.ca",
143+
"yahoo.co.in",
144+
"outlook.co.uk",
145+
"hotmail.co.uk",
143146
]
144147

145148
email = form_data.get("email", "").lower()
@@ -150,7 +153,6 @@ def submit(self, form_data: dict[str, Any]):
150153
yield rx.set_focus("email")
151154
return
152155

153-
154156
self.banned_email = False
155157
now = datetime.now()
156158
current_month = now.strftime("%Y-%m")
@@ -164,17 +166,43 @@ def submit(self, form_data: dict[str, Any]):
164166
"Company name": form_data.get("company_name", ""),
165167
"Phone Number": form_data.get("phone_number", ""),
166168
"Number of Employees": self.num_employees,
167-
"What internal tools are you looking to build?": form_data.get("internal_tools", ""),
169+
"What internal tools are you looking to build?": form_data.get(
170+
"internal_tools", ""
171+
),
168172
"Where did you first hear about Reflex?": self.referral_source,
169173
"month": current_month,
170174
"date": current_date,
171175
}
172176

173177
query_string = urllib.parse.urlencode(params)
174-
cal_url = f"https://cal.com/team/reflex/talk-to-a-reflex-expert?{query_string}"
178+
cal_url = (
179+
f"https://cal.com/team/reflex/talk-to-a-reflex-expert?{query_string}"
180+
)
181+
182+
yield QuoteFormState.send_demo_event(form_data)
175183

176184
return rx.redirect(cal_url)
177185

186+
@rx.event(background=True)
187+
async def send_demo_event(self, form_data: dict[str, Any]):
188+
first_name = form_data.get("first_name", "")
189+
last_name = form_data.get("last_name", "")
190+
await send_data_to_posthog(
191+
DemoEvent(
192+
distinct_id=f"{first_name} {last_name}",
193+
first_name=first_name,
194+
last_name=last_name,
195+
company_email=form_data.get("email", ""),
196+
phone_number=form_data.get("phone_number", ""),
197+
job_title=form_data.get("job_title", ""),
198+
company_name=form_data.get("company_name", ""),
199+
num_employees=self.num_employees,
200+
internal_tools=form_data.get("internal_tools", ""),
201+
referral_source=self.referral_source,
202+
)
203+
)
204+
205+
178206
def quote_input(placeholder: str, name: str, **props):
179207
return rx.el.input(
180208
placeholder=placeholder,
@@ -183,7 +211,10 @@ def quote_input(placeholder: str, name: str, **props):
183211
**props,
184212
)
185213

186-
def form_field(label: str, input_component, required: bool = False, class_name: str = "mb-6"):
214+
215+
def form_field(
216+
label: str, input_component, required: bool = False, class_name: str = "mb-6"
217+
):
187218
"""Reusable form field component with label and input."""
188219
label_text = f"{label} {'*' if required else ''}"
189220
return rx.box(
@@ -192,7 +223,15 @@ def form_field(label: str, input_component, required: bool = False, class_name:
192223
class_name=class_name,
193224
)
194225

195-
def text_input_field(label: str, name: str, placeholder: str, required: bool = False, input_type: str = "text", class_name: str = "mb-6"):
226+
227+
def text_input_field(
228+
label: str,
229+
name: str,
230+
placeholder: str,
231+
required: bool = False,
232+
input_type: str = "text",
233+
class_name: str = "mb-6",
234+
):
196235
"""Helper for creating text input fields."""
197236
input_component = quote_input(
198237
name=name,
@@ -202,13 +241,26 @@ def text_input_field(label: str, name: str, placeholder: str, required: bool = F
202241
)
203242
return form_field(label, input_component, required, class_name)
204243

205-
def select_field(label: str, name: str, options: list, placeholder: str, required: bool = False, state_var: str = ""):
244+
245+
def select_field(
246+
label: str,
247+
name: str,
248+
options: list,
249+
placeholder: str,
250+
required: bool = False,
251+
state_var: str = "",
252+
):
206253
"""Helper for creating custom select fields."""
207254
# Create scroll area with selectable options
208255
scroll_content = rx.box(
209256
*[
210257
select_item(
211-
content=(option, lambda opt=option, var=state_var: QuoteFormState.set_select_value(var, opt)),
258+
content=(
259+
option,
260+
lambda opt=option, var=state_var: QuoteFormState.set_select_value(
261+
var, opt
262+
),
263+
),
212264
size="sm",
213265
variant="selectable",
214266
class_name="w-full justify-start px-4 py-2 hover:bg-slate-2 rounded-md",
@@ -231,6 +283,7 @@ def select_field(label: str, name: str, options: list, placeholder: str, require
231283
)
232284
return form_field(label, select_component, required)
233285

286+
234287
def textarea_field(label: str, name: str, placeholder: str, required: bool = False):
235288
"""Helper for creating textarea fields."""
236289
textarea_component = rx.el.textarea(
@@ -241,6 +294,7 @@ def textarea_field(label: str, name: str, placeholder: str, required: bool = Fal
241294
)
242295
return form_field(label, textarea_component, required)
243296

297+
244298
def custom_quote_form() -> rx.Component:
245299
"""Custom quote form component with clean, maintainable structure."""
246300
return rx.box(
@@ -266,44 +320,108 @@ def custom_quote_form() -> rx.Component:
266320
rx.el.form(
267321
# Personal Information
268322
rx.el.div(
269-
text_input_field("First name", "first_name", "John", required=True, class_name="mb-0"),
270-
text_input_field("Last name", "last_name", "Smith", required=True, class_name="mb-0"),
323+
text_input_field(
324+
"First name",
325+
"first_name",
326+
"John",
327+
required=True,
328+
class_name="mb-0",
329+
),
330+
text_input_field(
331+
"Last name",
332+
"last_name",
333+
"Smith",
334+
required=True,
335+
class_name="mb-0",
336+
),
271337
class_name="flex-row flex gap-x-2 mb-6",
272338
),
273339
rx.cond(
274340
QuoteFormState.banned_email,
275341
rx.box(
276342
rx.el.div(
277-
rx.text("Business email", class_name="text-slate-11 text-sm font-medium mb-2"),
278-
rx.text("Personal emails not allowed!", class_name="text-red-8 text-sm font-medium mb-2"),
279-
class_name="flex flex-row items-center justify-between w-full",
343+
rx.text(
344+
"Business email",
345+
class_name="text-slate-11 text-sm font-medium mb-2",
346+
),
347+
rx.text(
348+
"Personal emails not allowed!",
349+
class_name="text-red-8 text-sm font-medium mb-2",
350+
),
351+
class_name="flex flex-row items-center justify-between w-full",
280352
),
281353
rx.el.input(
282354
placeholder="Personal emails not allowed!",
283355
name="email",
284356
class_name="box-border w-full border-2 border-red-5 bg-slate-1 px-6 pr-8 border rounded-[0.625rem] h-[2.25rem] font-medium text-slate-12 text-sm placeholder:text-slate-9 outline-none focus:outline-none caret-slate-12 peer pl-2.5 disabled:cursor-not-allowed disabled:border disabled:border-slate-5 disabled:!bg-slate-3 disabled:text-slate-8 disabled:placeholder:text-slate-8",
285-
)
357+
),
358+
),
359+
text_input_field(
360+
"Business email",
361+
"email",
362+
363+
required=True,
364+
input_type="email",
286365
),
287-
text_input_field("Business email", "email", "[email protected]", required=True, input_type="email"),
288366
),
289367
rx.el.div(
290-
text_input_field("Job title", "job_title", "CTO", required=True, class_name="mb-0"),
291-
text_input_field("Company name", "company_name", "Pynecone, Inc.", required=True, class_name="mb-0"),
368+
text_input_field(
369+
"Job title",
370+
"job_title",
371+
"CTO",
372+
required=True,
373+
class_name="mb-0",
374+
),
375+
text_input_field(
376+
"Company name",
377+
"company_name",
378+
"Pynecone, Inc.",
379+
required=True,
380+
class_name="mb-0",
381+
),
292382
class_name="flex-row flex gap-x-2 mb-6",
293383
),
294-
295-
text_input_field("Phone number", "phone_number", "(555) 123-4567", required=True, input_type="tel"),
296-
384+
text_input_field(
385+
"Phone number",
386+
"phone_number",
387+
"(555) 123-4567",
388+
required=True,
389+
input_type="tel",
390+
),
297391
# Project Details
298-
textarea_field("What are you looking to build?", "internal_tools", "Please list any apps, requirements, or data sources you plan on using", required=True),
299-
392+
textarea_field(
393+
"What are you looking to build?",
394+
"internal_tools",
395+
"Please list any apps, requirements, or data sources you plan on using",
396+
required=True,
397+
),
300398
# Company Size and Referral
301399
rx.el.div(
302-
select_field("Number of employees", "num_employees", ["1-10", "11-50", "51-100", "101-500", "500+"], "500+", required=True, state_var="num_employees"),
303-
select_field("How did you hear about us?", "referral_source", ["Google Search", "Social Media", "Word of Mouth", "Blog", "Conference", "Other"], "Google Search", required=True, state_var="referral_source"),
400+
select_field(
401+
"Number of employees",
402+
"num_employees",
403+
["1-10", "11-50", "51-100", "101-500", "500+"],
404+
"500+",
405+
required=True,
406+
state_var="num_employees",
407+
),
408+
select_field(
409+
"How did you hear about us?",
410+
"referral_source",
411+
[
412+
"Google Search",
413+
"Social Media",
414+
"Word of Mouth",
415+
"Blog",
416+
"Conference",
417+
"Other",
418+
],
419+
"Google Search",
420+
required=True,
421+
state_var="referral_source",
422+
),
304423
class_name="w-full flex-row flex flex-wrap justify-between mb-6",
305424
),
306-
307425
# Submit button
308426
button(
309427
"Submit",
@@ -326,6 +444,7 @@ def custom_quote_form() -> rx.Component:
326444
class_name="py-12 sm:py-20 px-4 sm:px-8",
327445
)
328446

447+
329448
def header() -> rx.Component:
330449
return rx.box(
331450
custom_quote_form(),

0 commit comments

Comments
 (0)