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
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 (
);
}
// ---------------------------------------------------------------------------
// 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.
Book call back
clint@wealthx.au
);
}