import React, { useState } from "react"; import { CalendarIcon, CheckIcon, CreditCardIcon, MailIcon, PhoneIcon, } from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "./button"; import { Checkbox } from "./checkbox"; import { Field, FieldError, FieldLabel } from "./field"; import { Input } from "./input"; import { Separator } from "./separator"; import { PasswordField, SectionHeading } from "./signup-form-primitives"; // --------------------------------------------------------------------------- // Plan model (wealthx.au/pricing) // --------------------------------------------------------------------------- // // The plan selector shows 4 plans, but onboarding has only 2 profiles: // broker_starter / broker_growth -> "broker" (team, aggregator, AI, extension) // accountant / planner -> "planner" (no team, channels only) // Enterprise is not self-serve — a system admin adds the company manually. export type PlanId = | "broker_starter" | "broker_growth" | "accountant" | "planner"; export type PlanProfile = "broker" | "planner"; export interface PlanConfig { id: PlanId; name: string; description: string; price: number; profile: PlanProfile; features: string[]; } export const PLAN_CONFIG: PlanConfig[] = [ { id: "broker_starter", name: "Broker Starter", description: "Everything a solo broker needs to get started.", price: 75, profile: "broker", features: [ "50 active connections (+$1 per additional)", "Website templates", "Digital fact find", "CRM Kanban", "Client wealth app", "Email support", ], }, { id: "broker_growth", name: "Broker Growth", description: "AI and automation for scaling brokerages.", price: 99, profile: "broker", features: [ "Everything in Starter", "WealthX AI", "Aggregator sync (Connective, AFG…)", "Chrome / Edge co-pilot", "Priority support", ], }, { id: "accountant", name: "Accountant", description: "Client portal and document management.", price: 79, profile: "planner", features: [ "Unlimited client connections", "Document vault", "Tax & accounting workflow", "Client wealth app", "Email support", ], }, { id: "planner", name: "Planner", description: "SOA tools and financial planning workflow.", price: 79, profile: "planner", features: [ "Unlimited client connections", "SOA generation", "Financial planning workflow", "Client wealth app", "Email support", ], }, ]; /** Resolve the onboarding profile (broker vs planner) for a chosen plan. */ export function getPlanProfile(id: PlanId): PlanProfile { return PLAN_CONFIG.find((p) => p.id === id)?.profile ?? "broker"; } // --------------------------------------------------------------------------- // Shared primitives (internal) // --------------------------------------------------------------------------- function PlanCard({ plan, isSelected, priceUnit, onClick, }: { plan: PlanConfig; isSelected: boolean; priceUnit: string; onClick: () => void; }) { return ( ); } function CardDetailsBlock() { return (
Card Number
•••• •••• •••• 4242
Expiry
MM / YY
CVC
•••

Powered by Stripe. Your payment information is encrypted and secure.

); } // --------------------------------------------------------------------------- // Step 1 — Create Account // --------------------------------------------------------------------------- export interface PersonalDetailsStepProps { onValidChange?: (valid: boolean) => void; /** When provided the email field is pre-filled and disabled. */ lockedEmail?: string; } export function PersonalDetailsStep({ onValidChange, lockedEmail, }: PersonalDetailsStepProps) { const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [email, setEmail] = useState(""); const [emailError, setEmailError] = useState(""); const [emailTouched, setEmailTouched] = useState(false); const [passwordValue, setPasswordValue] = useState(""); const [confirmValue, setConfirmValue] = useState(""); const [termsAccepted, setTermsAccepted] = useState(false); const validateEmail = (v: string) => { if (v.length > 0 && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) return "Please enter a valid email address"; return ""; }; const passwordRules = (v: string) => v.length >= 8 && /[A-Z]/.test(v) && /[a-z]/.test(v) && /[0-9]/.test(v) && /[^A-Za-z0-9]/.test(v); const emailValid = lockedEmail !== undefined || (email.length > 0 && validateEmail(email) === ""); const passwordValid = passwordRules(passwordValue); const confirmPasswordValid = confirmValue.length > 0 && confirmValue === passwordValue; React.useEffect(() => { onValidChange?.( firstName.trim().length > 0 && lastName.trim().length > 0 && emailValid && passwordValid && confirmPasswordValid && termsAccepted, ); }, [ firstName, lastName, emailValid, passwordValid, confirmPasswordValid, termsAccepted, onValidChange, ]); return (
First Name* setFirstName(e.target.value)} />
Last Name* setLastName(e.target.value)} />
Email Address* {lockedEmail !== undefined ? ( ) : ( { setEmail(e.target.value); if (emailTouched) setEmailError(validateEmail(e.target.value)); }} onBlur={() => { setEmailTouched(true); setEmailError(validateEmail(email)); }} /> )}
{emailTouched && emailError && {emailError}}
Phone Number
setTermsAccepted(!!v)} />
); } // --------------------------------------------------------------------------- // Step 2 — Plan & Payment // --------------------------------------------------------------------------- export interface PlanPaymentStepProps { /** Selected plan — preselected from the landing page, re-selectable here. */ value: PlanId; onChange: (id: PlanId) => void; } export function PlanPaymentStep({ value, onChange }: PlanPaymentStepProps) { return (
Select Plan
{PLAN_CONFIG.map((plan) => ( onChange(plan.id)} /> ))}

No lock-in contract · Cancel anytime · 30-day free trial

Payment Details
); } // --------------------------------------------------------------------------- // Step 3 — Confirmation // --------------------------------------------------------------------------- function SummaryRow({ label, value }: { label: string; value: string }) { return (
{label} {value}
); } export interface SignupConfirmationStepProps { name: string; email: string; planId: PlanId; /** Masked card brand + last 4 digits — shown read-only. */ cardBrand?: string; cardLast4?: string; /** Jump back to an earlier step to edit. */ onEditAccount?: () => void; onEditPlan?: () => void; } export function SignupConfirmationStep({ name, email, planId, cardBrand = "Visa", cardLast4 = "4242", onEditAccount, onEditPlan, }: SignupConfirmationStepProps) { const plan = PLAN_CONFIG.find((p) => p.id === planId)!; return (

Review your details before we create your account. To make changes, go back to the relevant step.

Account {onEditAccount && ( )}
Plan & Payment {onEditPlan && ( )}
{cardBrand} •••• {cardLast4}
); } // --------------------------------------------------------------------------- // Success screen // --------------------------------------------------------------------------- export function BackofficeSuccessStep() { return (

You're all set!

We've emailed you the next steps to set up your workspace. Let's get you onboarded.

Contact us anytime if you have any concerns.

0416 338 438
Book call back
clint@wealthx.au
); }