From b9ece3e6cc36b520beb7403629ed8eb6a64c815e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 12 Apr 2026 01:43:52 +0000 Subject: [PATCH] feat: Add premium marketers page + 3-phase onboarding flow Marketers page (435 lines): - Hero with gradient background + bilingual text - Stats bar (avg commission, active marketers, total paid) - 4 benefit cards (instant commission, pro tools, support, transparency) - 3-step how-it-works section - Commission tiers (Bronze 10%, Silver 15%, Gold 20%) - 2 Arabic testimonials - 5 FAQ accordion items - Registration form with +966 phone - All bilingual with useI18n Onboarding flow (429 lines): - Phase 1: Welcome + 2-question survey (role + industry) - Phase 2: Guided first deal creation with Saudi sample data - Phase 3: Setup checklist (contacts, WhatsApp, pipeline, team) - Celebration animation on deal creation - Progress tracking - All bilingual with RTL support https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj --- .../src/components/dealix/marketers-page.tsx | 435 ++++++++++++++++++ .../src/components/dealix/onboarding-flow.tsx | 429 +++++++++++++++++ 2 files changed, 864 insertions(+) create mode 100644 salesflow-saas/frontend/src/components/dealix/marketers-page.tsx create mode 100644 salesflow-saas/frontend/src/components/dealix/onboarding-flow.tsx diff --git a/salesflow-saas/frontend/src/components/dealix/marketers-page.tsx b/salesflow-saas/frontend/src/components/dealix/marketers-page.tsx new file mode 100644 index 00000000..95829706 --- /dev/null +++ b/salesflow-saas/frontend/src/components/dealix/marketers-page.tsx @@ -0,0 +1,435 @@ +'use client'; + +import { useState, useRef } from 'react'; +import { motion, useInView } from 'framer-motion'; +import { clsx } from 'clsx'; +import { + Zap, Wrench, HeadphonesIcon, Eye, + UserPlus, Share2, Coins, + Award, ChevronDown, ChevronUp, + LayoutDashboard, Link2, FileText, BarChart3, + Star, Quote, Phone, Mail, User, +} from 'lucide-react'; +import { useI18n } from '@/i18n'; + +/* ---------- Animation Helpers ---------- */ +const fadeUp = { + hidden: { opacity: 0, y: 24 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.5 } }, +}; + +const stagger = { + hidden: {}, + visible: { transition: { staggerChildren: 0.1 } }, +}; + +function Section({ children, className }: { children: React.ReactNode; className?: string }) { + const ref = useRef(null); + const inView = useInView(ref, { once: true, margin: '-60px' }); + return ( + + {children} + + ); +} + +function GlassCard({ children, className }: { children: React.ReactNode; className?: string }) { + return ( + + {children} + + ); +} + +/* ---------- FAQ Accordion ---------- */ +function FaqItem({ question, answer }: { question: string; answer: string }) { + const [open, setOpen] = useState(false); + return ( +
+ + +

{answer}

+
+
+ ); +} + +/* ---------- Main ---------- */ +function MarketersPage() { + const { t, dir, isArabic } = useI18n(); + const [form, setForm] = useState({ name: '', phone: '', email: '' }); + const [submitting, setSubmitting] = useState(false); + const [submitted, setSubmitted] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setSubmitting(true); + await new Promise((r) => setTimeout(r, 1500)); + setSubmitting(false); + setSubmitted(true); + }; + + const benefitIcons = [Zap, Wrench, HeadphonesIcon, Eye]; + const benefitKeys = [ + { title: 'benefitInstantCommission', desc: 'benefitInstantCommissionDesc' }, + { title: 'benefitProTools', desc: 'benefitProToolsDesc' }, + { title: 'benefitSupport', desc: 'benefitSupportDesc' }, + { title: 'benefitTransparency', desc: 'benefitTransparencyDesc' }, + ]; + + const stepIcons = [UserPlus, Share2, Coins]; + const stepKeys = [ + { title: 'step1Title', desc: 'step1Desc' }, + { title: 'step2Title', desc: 'step2Desc' }, + { title: 'step3Title', desc: 'step3Desc' }, + ]; + + const tiers = [ + { key: 'tierBronze', desc: 'tierBronzeDesc', pct: '10%', color: 'from-amber-700 to-amber-900', badge: 'bg-amber-700/40 text-amber-300' }, + { key: 'tierSilver', desc: 'tierSilverDesc', pct: '15%', color: 'from-slate-400 to-slate-600', badge: 'bg-slate-500/40 text-slate-200' }, + { key: 'tierGold', desc: 'tierGoldDesc', pct: '20%', color: 'from-amber-400 to-yellow-500', badge: 'bg-amber-400/30 text-amber-200' }, + ]; + + const toolIcons = [LayoutDashboard, Link2, FileText, BarChart3]; + const toolKeys = ['toolDashboard', 'toolLinks', 'toolTemplates', 'toolReports']; + + const faqs = Array.from({ length: 5 }, (_, i) => ({ + q: t(`marketersPage.faq${i + 1}Q`), + a: t(`marketersPage.faq${i + 1}A`), + })); + + return ( +
+ {/* ===== HERO ===== */} +
+
+
+
+ + {t('marketersPage.heroTitle')} + + + {t('marketersPage.heroSubtitle')} + +
+
+ + {/* ===== STATS BAR ===== */} +
+
+ + {[ + { label: t('marketersPage.statsAvgCommission'), value: isArabic ? '٤,٢٠٠ ر.س' : 'SAR 4,200' }, + { label: t('marketersPage.statsActiveMarketers'), value: isArabic ? '+١٢٠' : '120+' }, + { label: t('marketersPage.statsTotalPaid'), value: isArabic ? '+٢.٥ مليون ر.س' : 'SAR 2.5M+' }, + ].map((stat) => ( +
+

{stat.value}

+

{stat.label}

+
+ ))} +
+
+
+ + {/* ===== BENEFITS ===== */} +
+
+ + {t('marketersPage.benefitsTitle')} + +
+ {benefitKeys.map((bk, i) => { + const Icon = benefitIcons[i]; + return ( + +
+ +
+

+ {t(`marketersPage.${bk.title}`)} +

+

+ {t(`marketersPage.${bk.desc}`)} +

+
+ ); + })} +
+
+
+ + {/* ===== HOW IT WORKS ===== */} +
+
+ + {t('marketersPage.howItWorksTitle')} + +
+ {stepKeys.map((sk, i) => { + const Icon = stepIcons[i]; + return ( + +
+ +
+ + {i + 1} + +

+ {t(`marketersPage.${sk.title}`)} +

+

+ {t(`marketersPage.${sk.desc}`)} +

+
+ ); + })} +
+
+
+ + {/* ===== COMMISSION TIERS ===== */} +
+
+ + {t('marketersPage.tiersTitle')} + +
+ {tiers.map((tier) => ( + +
+
+ + {t(`marketersPage.${tier.key}`)} + +

{tier.pct}

+

{t('marketersPage.tierCommission')}

+

{t(`marketersPage.${tier.desc}`)}

+
+ + ))} +
+
+
+ + {/* ===== TESTIMONIALS ===== */} +
+
+ + {t('marketersPage.testimonialsTitle')} + +
+ {[1, 2].map((n) => ( + + +

+ {t(`marketersPage.testimonial${n}Text`)} +

+
+
+ {t(`marketersPage.testimonial${n}Name`).charAt(0)} +
+
+

+ {t(`marketersPage.testimonial${n}Name`)} +

+
+ + + {t(`marketersPage.testimonial${n}Role`)} + +
+
+
+
+ ))} +
+
+
+ + {/* ===== TOOLS PREVIEW ===== */} +
+
+ + {t('marketersPage.toolsTitle')} + + + {toolKeys.map((tk, i) => { + const Icon = toolIcons[i]; + return ( +
+ +

+ {t(`marketersPage.${tk}`)} +

+
+ ); + })} +
+
+
+ + {/* ===== FAQ ===== */} +
+
+ + {t('marketersPage.faqTitle')} + + + {faqs.map((faq, i) => ( + + ))} + +
+
+ + {/* ===== CTA + FORM ===== */} +
+
+ + {t('marketersPage.ctaTitle')} + + + {submitted ? ( + + +

{t('marketersPage.formSuccess')}

+
+ ) : ( + +
+ + setForm((f) => ({ ...f, name: e.target.value }))} + placeholder={t('marketersPage.formNamePlaceholder')} + className={clsx( + 'w-full rounded-xl bg-white/5 border border-white/10 ps-10 pe-4 py-3', + 'text-sm text-white placeholder:text-slate-500', + 'focus:outline-none focus:ring-2 focus:ring-teal-400/50 focus:border-transparent', + 'transition-all', + )} + /> +
+
+ + setForm((f) => ({ ...f, phone: e.target.value }))} + placeholder={t('marketersPage.formPhonePlaceholder')} + className={clsx( + 'w-full rounded-xl bg-white/5 border border-white/10 ps-10 pe-4 py-3', + 'text-sm text-white placeholder:text-slate-500 text-start', + 'focus:outline-none focus:ring-2 focus:ring-teal-400/50 focus:border-transparent', + 'transition-all', + )} + /> +
+
+ + setForm((f) => ({ ...f, email: e.target.value }))} + placeholder={t('marketersPage.formEmailPlaceholder')} + className={clsx( + 'w-full rounded-xl bg-white/5 border border-white/10 ps-10 pe-4 py-3', + 'text-sm text-white placeholder:text-slate-500 text-start', + 'focus:outline-none focus:ring-2 focus:ring-teal-400/50 focus:border-transparent', + 'transition-all', + )} + /> +
+ + {submitting ? t('marketersPage.formSubmitting') : t('marketersPage.ctaButton')} + +
+ )} +
+
+
+ ); +} + +export { MarketersPage }; diff --git a/salesflow-saas/frontend/src/components/dealix/onboarding-flow.tsx b/salesflow-saas/frontend/src/components/dealix/onboarding-flow.tsx new file mode 100644 index 00000000..76f1def6 --- /dev/null +++ b/salesflow-saas/frontend/src/components/dealix/onboarding-flow.tsx @@ -0,0 +1,429 @@ +'use client'; + +import { useState, useCallback } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { clsx } from 'clsx'; +import { + UserCircle, Building2, CheckCircle2, PartyPopper, + Import, MessageCircle, GitBranch, Users, + ChevronLeft, ChevronRight, Briefcase, Sparkles, +} from 'lucide-react'; +import { useI18n } from '@/i18n'; + +/* ---------- Types ---------- */ +type Phase = 'welcome' | 'firstValue' | 'checklist'; +type Role = 'salesManager' | 'salesRep' | 'executive' | 'other'; +type Industry = 'realEstate' | 'automotive' | 'healthcare' | 'services' | 'other'; + +interface OnboardingFlowProps { + onComplete?: () => void; + className?: string; +} + +/* ---------- Animation ---------- */ +const slideVariants = { + enter: (dir: number) => ({ x: dir > 0 ? 80 : -80, opacity: 0 }), + center: { x: 0, opacity: 1 }, + exit: (dir: number) => ({ x: dir > 0 ? -80 : 80, opacity: 0 }), +}; + +/* ---------- Phase 1: Welcome ---------- */ +function WelcomePhase({ + role, + setRole, + industry, + setIndustry, + onNext, +}: { + role: Role | null; + setRole: (r: Role) => void; + industry: Industry | null; + setIndustry: (i: Industry) => void; + onNext: () => void; +}) { + const { t, isArabic } = useI18n(); + const [step, setStep] = useState<'role' | 'industry'>('role'); + + const roles: { key: Role; icon: typeof UserCircle }[] = [ + { key: 'salesManager', icon: UserCircle }, + { key: 'salesRep', icon: Briefcase }, + { key: 'executive', icon: Building2 }, + { key: 'other', icon: Users }, + ]; + + const industries: { key: Industry; label: string }[] = [ + { key: 'realEstate', label: t('onboarding.industryRealEstate') }, + { key: 'automotive', label: t('onboarding.industryAutomotive') }, + { key: 'healthcare', label: t('onboarding.industryHealthcare') }, + { key: 'services', label: t('onboarding.industryServices') }, + { key: 'other', label: t('onboarding.industryOther') }, + ]; + + const roleLabels: Record = { + salesManager: t('onboarding.roleSalesManager'), + salesRep: t('onboarding.roleSalesRep'), + executive: t('onboarding.roleExecutive'), + other: t('onboarding.roleOther'), + }; + + return ( +
+ +

{t('onboarding.welcomeTitle')}

+

{t('onboarding.welcomeSubtitle')}

+ + + {step === 'role' ? ( + +

+ {t('onboarding.roleQuestion')} +

+
+ {roles.map((r) => { + const Icon = r.icon; + const selected = role === r.key; + return ( + + ); + })} +
+
+ ) : ( + +

+ {t('onboarding.industryQuestion')} +

+
+ {industries.map((ind) => { + const selected = industry === ind.key; + return ( + + ); + })} +
+ +
+ )} +
+
+ ); +} + +/* ---------- Phase 2: First Value ---------- */ +function FirstValuePhase({ onNext }: { onNext: () => void }) { + const { t, isArabic } = useI18n(); + const [created, setCreated] = useState(false); + + const handleCreate = () => { + setCreated(true); + setTimeout(onNext, 1800); + }; + + return ( +
+

{t('onboarding.firstValueTitle')}

+

{t('onboarding.firstValueSubtitle')}

+ + + {!created ? ( + +
+
+ +
+ {t('onboarding.sampleDealName')} +
+
+
+ +
+ {isArabic ? 'ر.س' : 'SAR'} {t('onboarding.sampleDealValue')} +
+
+
+ +
+ {t('onboarding.sampleContactName')} — {t('onboarding.sampleCompany')} +
+
+
+ + + {t('onboarding.createDeal')} + +
+ ) : ( + + + + +

{t('onboarding.celebration')}

+

{t('onboarding.dealCreated')}

+
+ )} +
+
+ ); +} + +/* ---------- Phase 3: Checklist ---------- */ +interface ChecklistItem { + key: string; + label: string; + icon: typeof Import; + done: boolean; +} + +function ChecklistPhase({ onComplete }: { onComplete?: () => void }) { + const { t } = useI18n(); + + const [items, setItems] = useState([ + { key: 'import', label: t('onboarding.checkImportContacts'), icon: Import, done: false }, + { key: 'whatsapp', label: t('onboarding.checkConnectWhatsApp'), icon: MessageCircle, done: false }, + { key: 'pipeline', label: t('onboarding.checkSetupPipeline'), icon: GitBranch, done: false }, + { key: 'team', label: t('onboarding.checkInviteTeam'), icon: Users, done: false }, + ]); + + const doneCount = items.filter((i) => i.done).length; + const progress = Math.round((doneCount / items.length) * 100); + + const toggleItem = useCallback((key: string) => { + setItems((prev) => + prev.map((item) => + item.key === key ? { ...item, done: !item.done } : item, + ), + ); + }, []); + + return ( +
+

{t('onboarding.checklistTitle')}

+ + {/* Progress */} +
+
+ +
+ + {progress}% {t('onboarding.checklistProgress')} + +
+ + {/* Items */} +
    + {items.map((item) => { + const Icon = item.icon; + return ( + toggleItem(item.key)} + > + {item.done ? ( + + ) : ( +
    + )} + + + {item.label} + + + ); + })} +
+ + {progress === 100 && ( + + + {t('common.getStarted')} + + + )} +
+ ); +} + +/* ---------- Main Onboarding Flow ---------- */ +function OnboardingFlow({ onComplete, className }: OnboardingFlowProps) { + const { dir } = useI18n(); + const [phase, setPhase] = useState('welcome'); + const [direction, setDirection] = useState(1); + const [role, setRole] = useState(null); + const [industry, setIndustry] = useState(null); + + const goTo = useCallback((next: Phase) => { + const order: Phase[] = ['welcome', 'firstValue', 'checklist']; + setDirection(order.indexOf(next) > order.indexOf(phase) ? 1 : -1); + setPhase(next); + }, [phase]); + + const phases: Phase[] = ['welcome', 'firstValue', 'checklist']; + const currentIdx = phases.indexOf(phase); + + return ( +
+ {/* Phase indicators */} +
+ {phases.map((p, i) => ( +
+ ))} +
+ + {/* Content */} + + + {phase === 'welcome' && ( + goTo('firstValue')} + /> + )} + {phase === 'firstValue' && ( + goTo('checklist')} /> + )} + {phase === 'checklist' && ( + + )} + + +
+ ); +} + +export { OnboardingFlow }; +export type { OnboardingFlowProps };