From 2585e76b4af7c7392e49745e855ac424808c0037 Mon Sep 17 00:00:00 2001 From: internxt-yu Date: Tue, 6 Oct 2026 15:53:10 +0200 Subject: [PATCH 1/3] added components of LP --- src/components/shared/SignUpForm.tsx | 198 ++++++++++++++++++ .../shared/sections/CardsGridSection.tsx | 57 +++++ .../test-ppc-leads/ChoosePlanSection.tsx | 122 +++++++++++ 3 files changed, 377 insertions(+) create mode 100644 src/components/shared/SignUpForm.tsx create mode 100644 src/components/shared/sections/CardsGridSection.tsx create mode 100644 src/components/test-ppc-leads/ChoosePlanSection.tsx diff --git a/src/components/shared/SignUpForm.tsx b/src/components/shared/SignUpForm.tsx new file mode 100644 index 000000000..9ae7758ed --- /dev/null +++ b/src/components/shared/SignUpForm.tsx @@ -0,0 +1,198 @@ +import { useState } from 'react'; +import { Info, WarningCircle } from '@phosphor-icons/react'; +import Link from 'next/link'; + +import TextInput from '@/components/components/TextInput'; +import PrimaryButton from '@/components/components/PrimaryButton'; +import PasswordStrength from '@/components/components/PasswordStrength'; +import testPasswordStrength from '@/components/auth/testPasswordStrength'; +import { goToLoginURL, signup } from '@/lib/auth'; + +const DRIVE_WEB_LOGIN_URL = 'https://drive.internxt.com/login'; + +export interface SignUpFormText { + fields: { + email: { + label: string; + placeholder: string; + }; + password: { + label: string; + placeholder: string; + show: string; + hide: string; + strength: { + complexity: string; + length: string; + weak: string; + strong: string; + }; + }; + submit: string; + }; + info?: string; + disclaimer: { + text: string; + link: string; + }; + login?: { + text: string; + link: string; + }; +} + +interface SignUpFormProps { + textContent: SignUpFormText; + lang?: string; + error?: string; + loading?: boolean; + onSubmitEmail?: (email: string) => Promise | void; +} + +type PasswordState = { + tag: 'error' | 'warning' | 'success'; + label: string; +}; + +export const SignUpForm = ({ textContent, lang, error, loading, onSubmitEmail }: SignUpFormProps): JSX.Element => { + const [passwordState, setPasswordState] = useState(); + const [showPassword, setShowPassword] = useState(false); + + const getPasswordState = (password: string): PasswordState => { + const result = testPasswordStrength(password, ''); + + if (!result.valid) { + return { + tag: 'error', + label: + result['reason'] === 'NOT_COMPLEX_ENOUGH' + ? textContent.fields.password.strength.complexity + : textContent.fields.password.strength.length, + }; + } + + if (result.strength === 'medium') { + return { tag: 'warning', label: textContent.fields.password.strength.weak }; + } + + return { tag: 'success', label: textContent.fields.password.strength.strong }; + }; + + const onSubmit = async (event) => { + event.preventDefault(); + const form = event.target.elements; + + const state = getPasswordState(form.password.value); + setPasswordState(state); + + if (state.tag === 'error') return; + + const email = form.email.value; + + if (onSubmitEmail) { + try { + await onSubmitEmail(email); + } catch { + // Capturing the lead must never block the signup flow + } + } + + signup({ email, password: form.password.value }); + }; + + const checkPassword = (input) => setPasswordState(getPasswordState(input.target.value)); + + return ( +
+
+ + +
+ +
+ +
+ + +
+ {passwordState && } +
+ + {textContent.info && ( +
+ +

{textContent.info}

+
+ )} + + {error && ( +
+
+ +
+ {error} +
+ )} + + + +

+ {textContent.disclaimer.text}{' '} + + {textContent.disclaimer.link} + + {'. '} + {textContent.login && ( + <> + {textContent.login.text}{' '} + + + )} +

+ + ); +}; + +export default SignUpForm; diff --git a/src/components/shared/sections/CardsGridSection.tsx b/src/components/shared/sections/CardsGridSection.tsx new file mode 100644 index 000000000..b94962378 --- /dev/null +++ b/src/components/shared/sections/CardsGridSection.tsx @@ -0,0 +1,57 @@ +interface CardsGridSectionProps { + textContent: { + title: string; + description?: string; + cards: { + titles: string[]; + descriptions: string[]; + }; + }; + bgColor?: string; + cardColor?: string; + needsH2?: boolean; + needsH3?: boolean; + topSeparationBar?: boolean; +} + +export const CardsGridSection = ({ + textContent, + bgColor = 'bg-white', + cardColor = 'bg-white', + needsH2 = true, + needsH3 = true, + topSeparationBar = true, +}: CardsGridSectionProps): JSX.Element => { + const TitleTag = needsH2 ? 'h2' : 'p'; + const CardTitleTag = needsH3 ? 'h3' : 'p'; + + return ( +
+ {topSeparationBar && ( +
+ )} + +
+
+ {textContent.title} + {textContent.description && ( +

{textContent.description}

+ )} +
+ +
+ {textContent.cards.titles.map((title, index) => ( +
+ {title} +

+ {textContent.cards.descriptions[index]} +

+
+ ))} +
+
+
+ ); +}; + +export default CardsGridSection; diff --git a/src/components/test-ppc-leads/ChoosePlanSection.tsx b/src/components/test-ppc-leads/ChoosePlanSection.tsx new file mode 100644 index 000000000..87b7edc66 --- /dev/null +++ b/src/components/test-ppc-leads/ChoosePlanSection.tsx @@ -0,0 +1,122 @@ +import { Check, HandCoins, Headset, Keyhole } from '@phosphor-icons/react'; +import Link from 'next/link'; + +import { formatText } from '@/components/utils/format-text'; +import { ChoosePlanSection as ChoosePlanSectionText, PlanCard } from '@/assets/types/test-ppc-leads'; + +interface ChoosePlanSectionProps { + textContent: ChoosePlanSectionText; + currency: string; + minimumPrice: string; + loadingCards?: boolean; + getDealURL?: string; + signUpURL?: string; + sectionDetails?: string; + topSeparationBar?: boolean; +} + +const PriceSkeleton = () => ; + +const PlanFeatures = ({ features }: { features: string[] }) => ( +
+ {features.map((feature) => ( +
+ +

{feature}

+
+ ))} +
+); + +const PlanHeader = ({ card, price, isLoading }: { card: PlanCard; price: string; isLoading?: boolean }) => ( +
+
+

{card.label}

+ {card.badge && ( + + {card.badge} + + )} +
+
+

{card.storage}

+

+ {'ยท '} + {isLoading ? : price} +

+
+
+); + +export const ChoosePlanSection = ({ + textContent, + currency, + minimumPrice, + loadingCards = false, + getDealURL = '/fb', + signUpURL = '#heroSection', + sectionDetails = 'bg-white py-10 lg:py-20', + topSeparationBar = true, +}: ChoosePlanSectionProps): JSX.Element => { + const freePrice = formatText(textContent.freeCard.price, { currency }); + const premiumPrice = formatText(textContent.premiumCard.price, { currency, minimumPrice }); + + const guarantees = [ + { icon: Headset, text: textContent.guarantees.premiumSupport }, + { icon: HandCoins, text: textContent.guarantees.moneyBack }, + { icon: Keyhole, text: textContent.guarantees.openSource }, + ]; + + return ( +
+ {topSeparationBar && ( +
+ )} +
+
+

{textContent.title}

+

{textContent.description}

+
+ +
+
+
+ + +
+ + {textContent.freeCard.cta} + +
+ +
+
+ + +
+ + {textContent.premiumCard.cta} + +
+
+ +
+ {guarantees.map((guarantee) => ( +
+ +

{guarantee.text}

+
+ ))} +
+
+
+ ); +}; + +export default ChoosePlanSection; From 527082fed4564aa686314c18b8d1e35e2c91cb19 Mon Sep 17 00:00:00 2001 From: internxt-yu Date: Thu, 8 Oct 2026 12:44:31 +0200 Subject: [PATCH 2/3] change choosePlanSection title size --- src/components/test-ppc-leads/ChoosePlanSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/test-ppc-leads/ChoosePlanSection.tsx b/src/components/test-ppc-leads/ChoosePlanSection.tsx index 87b7edc66..c5982a4c8 100644 --- a/src/components/test-ppc-leads/ChoosePlanSection.tsx +++ b/src/components/test-ppc-leads/ChoosePlanSection.tsx @@ -74,7 +74,7 @@ export const ChoosePlanSection = ({ )}
-

{textContent.title}

+

{textContent.title}

{textContent.description}

From bafb4fb805dfe4161cf6b2af4fd492cfb00396c1 Mon Sep 17 00:00:00 2001 From: internxt-yu Date: Thu, 8 Oct 2026 13:32:44 +0200 Subject: [PATCH 3/3] removed comment and added console log because catch cant be empty --- src/components/shared/SignUpForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/shared/SignUpForm.tsx b/src/components/shared/SignUpForm.tsx index 9ae7758ed..4263efa29 100644 --- a/src/components/shared/SignUpForm.tsx +++ b/src/components/shared/SignUpForm.tsx @@ -92,8 +92,8 @@ export const SignUpForm = ({ textContent, lang, error, loading, onSubmitEmail }: if (onSubmitEmail) { try { await onSubmitEmail(email); - } catch { - // Capturing the lead must never block the signup flow + } catch (err) { + console.warn('Lead capture failed'); } }