Skip to content

Commit 14eaeba

Browse files
authored
ENG-6550: form patches to include linkedin and other stuff (#1466)
* form patches to include linkedin and other stuff * clean up unused state vars
1 parent 82f8483 commit 14eaeba

File tree

2 files changed

+101
-53
lines changed

2 files changed

+101
-53
lines changed

pcweb/pages/pricing/header.py

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
import reflex as rx
66
from reflex.event import EventType
7+
from reflex.experimental import ClientStateVar
78

89
from pcweb.components.hosting_banner import HostingBannerState
910
from pcweb.components.new_button import button
1011
from pcweb.constants import CAL_REQUEST_DEMO_URL
1112
from pcweb.pages.framework.views.companies import pricing_page_companies
1213
from pcweb.telemetry.postog_metrics import DemoEvent, send_data_to_posthog
1314

15+
ThankYouDialogState = ClientStateVar.create("thank_you_dialog_state", False)
16+
1417
SelectVariant = Literal["primary", "secondary", "outline", "transparent"]
1518
SelectSize = Literal["sm", "md", "lg"]
1619
SelectItemVariant = Literal["selectable", "actions", "projects"]
@@ -63,7 +66,6 @@ def select_item(
6366
),
6467
class_name="w-full outline-none focus:outline-none",
6568
)
66-
# return rx.el.button(text, **common_props)
6769

6870

6971
def select(
@@ -115,28 +117,30 @@ class QuoteFormState(rx.State):
115117
num_employees: str = "500+"
116118
referral_source: str = "Google Search"
117119
banned_email: bool = False
118-
banned_phone: bool = False
120+
banned_linkedin: bool = False
119121

120122
def set_select_value(self, field: str, value: str):
121123
"""Update the selected value for a given field."""
122124
setattr(self, field, value)
123125

126+
def is_small_company(self) -> bool:
127+
"""Check if company has 10 or fewer employees."""
128+
return self.num_employees in ["1", "2-5", "6-10"]
129+
124130
@rx.event
125131
def submit(self, form_data: dict[str, Any]):
126-
# Phone number validation
127-
phone = form_data.get("phone_number", "").strip()
128-
if phone:
129-
phone_pattern = r'^(\+?1[-.\s]?)?(\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4})|(\+[1-9]\d{0,3}[-.\s]?(\(?[0-9]{1,4}\)?[-.\s]?){1,5}[0-9]{1,4})$'
130-
131-
# Remove common formatting characters for validation
132-
clean_phone = re.sub(r'[^\d+]', '', phone)
133-
134-
if not re.match(phone_pattern, phone) or len(clean_phone) < 10 or len(clean_phone) > 15:
135-
self.banned_phone = True
136-
yield rx.set_focus("phone_number")
132+
# LinkedIn URL validation
133+
linkedin_url = form_data.get("linkedin_url", "").strip()
134+
if linkedin_url:
135+
# Basic LinkedIn URL validation
136+
linkedin_pattern = r'^https?://(www\.)?linkedin\.com/(in|company)/.+$'
137+
138+
if not re.match(linkedin_pattern, linkedin_url):
139+
self.banned_linkedin = True
140+
yield rx.set_focus("linkedin_url")
137141
return
138-
139-
self.banned_phone = False
142+
143+
self.banned_linkedin = False
140144

141145
# Email domain validation
142146
banned_domains = [
@@ -176,13 +180,22 @@ def submit(self, form_data: dict[str, Any]):
176180
notes_content = f"""
177181
Name: {form_data.get("first_name", "")} {form_data.get("last_name", "")}
178182
Business Email: {form_data.get("email", "")}
179-
Phone Number: {form_data.get("phone_number", "")}
183+
LinkedIn URL: {form_data.get("linkedin_url", "")}
180184
Job Title: {form_data.get("job_title", "")}
181185
Company Name: {form_data.get("company_name", "")}
182186
Number of Employees: {self.num_employees}
183187
Internal Tools to Build: {form_data.get("internal_tools", "")}
184188
How they heard about Reflex: {self.referral_source}"""
185189

190+
# Send to PostHog for all submissions
191+
yield QuoteFormState.send_demo_event(form_data)
192+
193+
# Check if it's a small company
194+
if self.is_small_company():
195+
yield ThankYouDialogState.push(True)
196+
return
197+
198+
# For larger companies, redirect to calendar booking
186199
params = {
187200
"email": form_data.get("email", ""),
188201
"name": f"{form_data.get('first_name', '')} {form_data.get('last_name', '')}",
@@ -192,10 +205,9 @@ def submit(self, form_data: dict[str, Any]):
192205
query_string = urllib.parse.urlencode(params)
193206
cal_url = f"{CAL_REQUEST_DEMO_URL}?{query_string}"
194207

195-
yield QuoteFormState.send_demo_event(form_data)
196-
197208
return rx.redirect(cal_url)
198209

210+
199211
@rx.event(background=True)
200212
async def send_demo_event(self, form_data: dict[str, Any]):
201213
first_name = form_data.get("first_name", "")
@@ -206,7 +218,7 @@ async def send_demo_event(self, form_data: dict[str, Any]):
206218
first_name=first_name,
207219
last_name=last_name,
208220
company_email=form_data.get("email", ""),
209-
phone_number=form_data.get("phone_number", ""),
221+
linkedin_url=form_data.get("linkedin_url", ""), # Updated from phone_number
210222
job_title=form_data.get("job_title", ""),
211223
company_name=form_data.get("company_name", ""),
212224
num_employees=self.num_employees,
@@ -264,22 +276,21 @@ def select_field(
264276
state_var: str = "",
265277
):
266278
"""Helper for creating custom select fields."""
279+
280+
def create_select_item(option, state_var):
281+
return select_item(
282+
content=(
283+
option,
284+
lambda: QuoteFormState.set_select_value(state_var, option),
285+
),
286+
size="sm",
287+
variant="selectable",
288+
class_name="w-full justify-start px-4 py-2 hover:bg-slate-2 rounded-md",
289+
)
290+
267291
# Create scroll area with selectable options
268292
scroll_content = rx.box(
269-
*[
270-
select_item(
271-
content=(
272-
option,
273-
lambda opt=option, var=state_var: QuoteFormState.set_select_value(
274-
var, opt
275-
),
276-
),
277-
size="sm",
278-
variant="selectable",
279-
class_name="w-full justify-start px-4 py-2 hover:bg-slate-2 rounded-md",
280-
)
281-
for option in options
282-
],
293+
*[create_select_item(option, state_var) for option in options],
283294
class_name="!pt-0 !mt-0 !max-h-[15rem] bg-slate-1 border border-slate-5 rounded-lg shadow-lg py-0 overflow-y-scroll w-full",
284295
)
285296

@@ -308,6 +319,47 @@ def textarea_field(label: str, name: str, placeholder: str, required: bool = Fal
308319
return form_field(label, textarea_component, required)
309320

310321

322+
def thank_you_modal() -> rx.Component:
323+
"""Thank you modal for small companies."""
324+
325+
return rx.dialog.root(
326+
rx.dialog.content(
327+
rx.box(
328+
rx.box(
329+
rx.text(
330+
"Thank You for Your Interest!",
331+
class_name="text-2xl font-semibold text-slate-12 font-sans",
332+
),
333+
rx.dialog.close(
334+
button(
335+
rx.icon("x", class_name="!text-slate-9"),
336+
variant="transparent",
337+
size="icon-sm",
338+
type="button",
339+
class_name="focus:outline-none",
340+
on_click=ThankYouDialogState.set_value(False)
341+
342+
),
343+
),
344+
class_name="flex flex-row items-center gap-2 justify-between w-full",
345+
),
346+
rx.text(
347+
"We've received your submission and our team will get back to you soon. We appreciate your interest in Reflex!",
348+
class_name="text-slate-9 font-medium text-sm",
349+
),
350+
class_name="flex flex-col w-full gap-y-4"
351+
),
352+
class_name="w-full",
353+
on_interact_outside=ThankYouDialogState.set_value(False),
354+
on_escape_key_down=ThankYouDialogState.set_value(False),
355+
356+
),
357+
open=ThankYouDialogState.value
358+
)
359+
360+
361+
362+
311363
def custom_quote_form() -> rx.Component:
312364
"""Custom quote form component with clean, maintainable structure."""
313365
return rx.box(
@@ -394,35 +446,36 @@ def custom_quote_form() -> rx.Component:
394446
),
395447
class_name="flex-row flex gap-x-2 mb-6",
396448
),
449+
# LinkedIn field (replacing phone number)
397450
rx.cond(
398-
QuoteFormState.banned_phone,
451+
QuoteFormState.banned_linkedin,
399452
rx.box(
400453
rx.el.div(
401454
rx.text(
402-
"Phone number",
455+
"LinkedIn profile URL",
403456
class_name="text-slate-11 text-sm font-medium mb-2",
404457
),
405458
rx.text(
406-
"Invalid phone number format!",
459+
"Invalid LinkedIn URL format!",
407460
class_name="text-red-8 text-sm font-medium mb-2",
408461
),
409462
class_name="flex flex-row items-center justify-between w-full",
410463
),
411464
rx.el.input(
412-
placeholder="Please enter a valid phone number",
413-
name="phone_number",
414-
type="tel",
465+
placeholder="Please enter a valid LinkedIn URL",
466+
name="linkedin_url",
467+
type="url",
415468
required=True,
416469
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",
417470
),
418471
class_name="mb-6",
419472
),
420473
text_input_field(
421-
"Phone number",
422-
"phone_number",
423-
"(555) 123-4567",
474+
"LinkedIn profile URL",
475+
"linkedin_url",
476+
"https://linkedin.com/in/your-profile",
424477
required=True,
425-
input_type="tel",
478+
input_type="url",
426479
),
427480
),
428481
# Project Details
@@ -437,7 +490,7 @@ def custom_quote_form() -> rx.Component:
437490
select_field(
438491
"Number of employees",
439492
"num_employees",
440-
["1-10", "11-50", "51-100", "101-500", "500+"],
493+
["1", "2-5", "6-10", "11-50", "51-100", "101-500", "500+"], # Updated options
441494
"500+",
442495
required=True,
443496
state_var="num_employees",
@@ -469,26 +522,21 @@ def custom_quote_form() -> rx.Component:
469522
),
470523
class_name="w-full space-y-6",
471524
on_submit=QuoteFormState.submit,
525+
reset_on_submit=True,
472526
),
473527
class_name="relative bg-slate-1 p-6 sm:p-8 rounded-2xl border-2 border-[--violet-9] shadow-lg w-full max-w-md mx-auto lg:max-w-none lg:mx-0",
474528
),
475529
class_name="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-16 max-w-7xl mx-auto items-start",
476530
),
531+
# Thank you modal
532+
thank_you_modal(),
477533
class_name="py-12 sm:py-20 px-4 sm:px-8",
478534
)
479535

480536

481537
def header() -> rx.Component:
482538
return rx.box(
483539
custom_quote_form(),
484-
# rx.el.h1(
485-
# "Get a custom quote",
486-
# class_name="gradient-heading font-semibold text-3xl lg:text-5xl text-center",
487-
# ),
488-
# rx.el.p(
489-
# "The complete platform for building and deploying your apps.",
490-
# class_name="text-slate-9 text-md lg:text-xl font-semibold text-center",
491-
# ),
492540
class_name="flex flex-col gap-2 justify-center items-center max-w-[64.19rem] 2xl:border-x border-slate-4 w-full -mb-10 "
493541
+ rx.cond(HostingBannerState.show_banner, "pt-[11rem]", "pt-[12rem]"),
494542
)

pcweb/telemetry/postog_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class DemoEvent(PosthogEvent):
2929
first_name: str
3030
last_name: str
3131
company_email: str
32-
phone_number: str
32+
linkedin_url: str
3333
job_title: str
3434
company_name: str
3535
num_employees: str

0 commit comments

Comments
 (0)