@afncdelacru/brady-chat 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1389 -1387
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1357 -1355
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/lib/BradyChatContext.tsx +1 -0
- package/src/lib/EnhancedBradyChat.tsx +34 -3
- package/src/lib/BranchProfitabilityCalculator.tsx +0 -615
- package/src/lib/CalculatorFollowUp.tsx +0 -200
- package/src/lib/CalculatorManager.tsx +0 -37
- package/src/lib/LOEarningsCalculator.tsx +0 -366
- package/src/lib/ProgressiveContactForm.tsx +0 -337
- package/src/lib/RecruitingFlowContext.tsx +0 -259
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { motion, AnimatePresence } from 'motion/react';
|
|
3
|
-
import { X, CheckCircle } from 'lucide-react';
|
|
4
|
-
import { useRecruitingFlow } from './RecruitingFlowContext';
|
|
5
|
-
import { ProgressiveContactForm } from './ProgressiveContactForm';
|
|
6
|
-
|
|
7
|
-
interface CalculatorFollowUpProps {
|
|
8
|
-
isOpen: boolean;
|
|
9
|
-
onClose: () => void;
|
|
10
|
-
isBranchManager: boolean;
|
|
11
|
-
clickedPrimaryCTA: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function CalculatorFollowUp({
|
|
15
|
-
isOpen,
|
|
16
|
-
onClose,
|
|
17
|
-
isBranchManager,
|
|
18
|
-
clickedPrimaryCTA
|
|
19
|
-
}: CalculatorFollowUpProps) {
|
|
20
|
-
const { intentData, trackAction } = useRecruitingFlow();
|
|
21
|
-
const [showForm, setShowForm] = useState(false);
|
|
22
|
-
const [submitted, setSubmitted] = useState(false);
|
|
23
|
-
|
|
24
|
-
// Determine which copy to show based on user type and action
|
|
25
|
-
const getFollowUpContent = () => {
|
|
26
|
-
if (isBranchManager) {
|
|
27
|
-
if (clickedPrimaryCTA) {
|
|
28
|
-
// State 4: BM clicked primary CTA
|
|
29
|
-
return {
|
|
30
|
-
header: "Let's Look at This Through a Branch Lens",
|
|
31
|
-
body: "We'll discuss how productivity, pull-through, and margin visibility impact branch economics—without assuming comp plans or payout structures.",
|
|
32
|
-
primaryCTA: "Schedule a Leadership Call",
|
|
33
|
-
secondaryCTA: "Request a Follow-Up Summary",
|
|
34
|
-
microCopy: null
|
|
35
|
-
};
|
|
36
|
-
} else {
|
|
37
|
-
// State 3: BM interacted but didn't click CTA
|
|
38
|
-
return {
|
|
39
|
-
header: "Curious how this plays out in practice?",
|
|
40
|
-
body: "Every branch is different. These drivers show where earnings typically improve—but execution matters.\n\nIf you want, we can review how these levers apply specifically to your branch.",
|
|
41
|
-
primaryCTA: "Review My Branch Scenario",
|
|
42
|
-
secondaryCTA: "Continue Exploring the Platform",
|
|
43
|
-
microCopy: null
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
if (clickedPrimaryCTA) {
|
|
48
|
-
// State 2: LO clicked primary CTA
|
|
49
|
-
return {
|
|
50
|
-
header: "Let's Review This Together",
|
|
51
|
-
body: "We'll review your inputs, talk through realistic volume drivers, and answer questions about how AFN supports producers like you.\n\nNo pressure—just clarity.",
|
|
52
|
-
primaryCTA: "Schedule a Private Conversation",
|
|
53
|
-
secondaryCTA: "Send Me a Summary",
|
|
54
|
-
microCopy: null
|
|
55
|
-
};
|
|
56
|
-
} else {
|
|
57
|
-
// State 1: LO interacted but didn't click CTA
|
|
58
|
-
return {
|
|
59
|
-
header: "Want to sanity-check these numbers?",
|
|
60
|
-
body: "This model shows how increased funded volume can impact income—but every LO's situation is different.\n\nIf you want, we can walk through your scenario together and pressure-test the assumptions.",
|
|
61
|
-
primaryCTA: "Talk Through My Scenario",
|
|
62
|
-
secondaryCTA: "Keep Exploring AFN",
|
|
63
|
-
microCopy: "No commitment. Just a conversation."
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const content = getFollowUpContent();
|
|
70
|
-
|
|
71
|
-
const handlePrimaryCTAClick = () => {
|
|
72
|
-
// Track high intent action
|
|
73
|
-
trackAction(
|
|
74
|
-
isBranchManager ? 'branch_scenario_requested' : 'lo_scenario_requested',
|
|
75
|
-
'high'
|
|
76
|
-
);
|
|
77
|
-
setShowForm(true);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const handleFormSubmit = (data: any) => {
|
|
81
|
-
console.log('Form submitted:', data);
|
|
82
|
-
setSubmitted(true);
|
|
83
|
-
// Here you would send data to your backend/CRM
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
if (!isOpen) return null;
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<AnimatePresence>
|
|
90
|
-
{isOpen && (
|
|
91
|
-
<>
|
|
92
|
-
{/* Backdrop */}
|
|
93
|
-
<motion.div
|
|
94
|
-
initial={{ opacity: 0 }}
|
|
95
|
-
animate={{ opacity: 1 }}
|
|
96
|
-
exit={{ opacity: 0 }}
|
|
97
|
-
onClick={!showForm ? onClose : undefined}
|
|
98
|
-
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50"
|
|
99
|
-
/>
|
|
100
|
-
|
|
101
|
-
{/* Follow-Up Panel */}
|
|
102
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 md:p-6 pointer-events-none">
|
|
103
|
-
<motion.div
|
|
104
|
-
key={showForm ? 'form' : 'copy'}
|
|
105
|
-
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
106
|
-
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
107
|
-
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
108
|
-
transition={{ duration: 0.2 }}
|
|
109
|
-
className="w-full max-w-lg max-h-[90vh] overflow-y-auto bg-white dark:bg-zinc-900 rounded-2xl shadow-2xl pointer-events-auto"
|
|
110
|
-
>
|
|
111
|
-
{/* Header */}
|
|
112
|
-
<div className="sticky top-0 bg-white dark:bg-zinc-900 px-6 py-4 border-b border-zinc-200 dark:border-zinc-800 flex items-start justify-between z-10">
|
|
113
|
-
<h2 className="text-xl md:text-2xl text-zinc-900 dark:text-white pr-8">
|
|
114
|
-
{submitted ? "Thanks — We'll Be In Touch" : content.header}
|
|
115
|
-
</h2>
|
|
116
|
-
<button
|
|
117
|
-
onClick={onClose}
|
|
118
|
-
className="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors"
|
|
119
|
-
aria-label="Close"
|
|
120
|
-
>
|
|
121
|
-
<X className="w-5 h-5 text-zinc-500" />
|
|
122
|
-
</button>
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Body */}
|
|
126
|
-
<div className="px-6 py-6">
|
|
127
|
-
{submitted ? (
|
|
128
|
-
// Success State
|
|
129
|
-
<div className="space-y-6 text-center">
|
|
130
|
-
<div className="flex justify-center">
|
|
131
|
-
<CheckCircle className="w-16 h-16 text-green-500" />
|
|
132
|
-
</div>
|
|
133
|
-
<div className="space-y-2">
|
|
134
|
-
<p className="text-base text-zinc-600 dark:text-zinc-400">
|
|
135
|
-
Thanks — we'll follow up shortly.
|
|
136
|
-
</p>
|
|
137
|
-
<p className="text-sm text-zinc-500 dark:text-zinc-500">
|
|
138
|
-
In the meantime, feel free to keep exploring.
|
|
139
|
-
</p>
|
|
140
|
-
</div>
|
|
141
|
-
<button
|
|
142
|
-
onClick={onClose}
|
|
143
|
-
className="w-full px-6 py-3 bg-gradient-to-r from-[#4399D1] to-[#2B7AB8] hover:from-[#2B7AB8] hover:to-[#4399D1] text-white rounded-lg transition-all duration-300 shadow-lg"
|
|
144
|
-
>
|
|
145
|
-
Continue Exploring
|
|
146
|
-
</button>
|
|
147
|
-
</div>
|
|
148
|
-
) : showForm ? (
|
|
149
|
-
// Form State (HIGH INTENT)
|
|
150
|
-
<div className="space-y-4">
|
|
151
|
-
<p className="text-sm text-zinc-600 dark:text-zinc-400">
|
|
152
|
-
{content.body.split('\n\n')[0]}
|
|
153
|
-
</p>
|
|
154
|
-
<ProgressiveContactForm
|
|
155
|
-
onSubmit={handleFormSubmit}
|
|
156
|
-
onCancel={() => setShowForm(false)}
|
|
157
|
-
primaryCTA={content.primaryCTA}
|
|
158
|
-
secondaryCTA="Go Back"
|
|
159
|
-
context={isBranchManager ? 'BM' : 'LO'}
|
|
160
|
-
/>
|
|
161
|
-
</div>
|
|
162
|
-
) : (
|
|
163
|
-
// Copy-Only State (MEDIUM INTENT)
|
|
164
|
-
<div className="space-y-6">
|
|
165
|
-
<p className="text-base text-zinc-600 dark:text-zinc-400 whitespace-pre-line">
|
|
166
|
-
{content.body}
|
|
167
|
-
</p>
|
|
168
|
-
|
|
169
|
-
{/* CTAs */}
|
|
170
|
-
<div className="space-y-3">
|
|
171
|
-
<button
|
|
172
|
-
onClick={handlePrimaryCTAClick}
|
|
173
|
-
className="w-full px-6 py-3 bg-gradient-to-r from-[#4399D1] to-[#2B7AB8] hover:from-[#2B7AB8] hover:to-[#4399D1] text-white rounded-lg transition-all duration-300 shadow-lg"
|
|
174
|
-
>
|
|
175
|
-
{content.primaryCTA}
|
|
176
|
-
</button>
|
|
177
|
-
<button
|
|
178
|
-
onClick={onClose}
|
|
179
|
-
className="w-full px-6 py-3 bg-white dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 border border-zinc-300 dark:border-zinc-700 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors"
|
|
180
|
-
>
|
|
181
|
-
{content.secondaryCTA}
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
|
|
185
|
-
{/* Micro Copy */}
|
|
186
|
-
{content.microCopy && (
|
|
187
|
-
<p className="text-xs text-zinc-500 dark:text-zinc-500 text-center">
|
|
188
|
-
{content.microCopy}
|
|
189
|
-
</p>
|
|
190
|
-
)}
|
|
191
|
-
</div>
|
|
192
|
-
)}
|
|
193
|
-
</div>
|
|
194
|
-
</motion.div>
|
|
195
|
-
</div>
|
|
196
|
-
</>
|
|
197
|
-
)}
|
|
198
|
-
</AnimatePresence>
|
|
199
|
-
);
|
|
200
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { useBradyChat } from '@afncdelacru/brady-chat';
|
|
5
|
-
import { LOEarningsCalculator } from './LOEarningsCalculator';
|
|
6
|
-
import { BranchProfitabilityCalculator } from './BranchProfitabilityCalculator';
|
|
7
|
-
// RouteContext removed for package isolation. Consumers must provide page context as props.
|
|
8
|
-
export function CalculatorManager({ isLOPage, isBMPage, calculatorOpen, setCalculatorOpen, modeData }: {
|
|
9
|
-
isLOPage: boolean;
|
|
10
|
-
isBMPage: boolean;
|
|
11
|
-
calculatorOpen: boolean;
|
|
12
|
-
setCalculatorOpen: (open: boolean) => void;
|
|
13
|
-
modeData?: any;
|
|
14
|
-
}) {
|
|
15
|
-
if (isLOPage) {
|
|
16
|
-
return (
|
|
17
|
-
<LOEarningsCalculator
|
|
18
|
-
isOpen={calculatorOpen}
|
|
19
|
-
onClose={() => setCalculatorOpen(false)}
|
|
20
|
-
isBranchManager={false}
|
|
21
|
-
// prefillData={modeData} // Add this prop if needed in LOEarningsCalculator
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
if (isBMPage) {
|
|
26
|
-
return (
|
|
27
|
-
<BranchProfitabilityCalculator
|
|
28
|
-
isOpen={calculatorOpen}
|
|
29
|
-
onClose={() => setCalculatorOpen(false)}
|
|
30
|
-
isPersonalized={false}
|
|
31
|
-
defaultVolume={250000000}
|
|
32
|
-
// prefillData={modeData} // Add this prop if needed in BranchProfitabilityCalculator
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import { motion, AnimatePresence } from 'motion/react';
|
|
3
|
-
import { X, CheckCircle } from 'lucide-react';
|
|
4
|
-
import { CalculatorFollowUp } from './CalculatorFollowUp';
|
|
5
|
-
import { ProgressiveContactForm } from './ProgressiveContactForm';
|
|
6
|
-
import { useRecruitingFlow } from './RecruitingFlowContext';
|
|
7
|
-
|
|
8
|
-
interface LOEarningsCalculatorProps {
|
|
9
|
-
isOpen: boolean;
|
|
10
|
-
onClose: () => void;
|
|
11
|
-
defaultMonthlyVolume?: number;
|
|
12
|
-
defaultCompensation?: number;
|
|
13
|
-
isBranchManager?: boolean; // Determines which follow-up copy to show
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function LOEarningsCalculator({
|
|
17
|
-
isOpen,
|
|
18
|
-
onClose,
|
|
19
|
-
defaultMonthlyVolume = 1000000,
|
|
20
|
-
defaultCompensation = 125,
|
|
21
|
-
isBranchManager = false
|
|
22
|
-
}: LOEarningsCalculatorProps) {
|
|
23
|
-
const { trackCalculatorOpen, trackCalculatorAdjustment, trackAction } = useRecruitingFlow();
|
|
24
|
-
const [monthlyVolume, setMonthlyVolume] = useState(defaultMonthlyVolume);
|
|
25
|
-
const [compensation, setCompensation] = useState(defaultCompensation);
|
|
26
|
-
const [hasInteracted, setHasInteracted] = useState(false);
|
|
27
|
-
const [clickedPrimaryCTA, setClickedPrimaryCTA] = useState(false);
|
|
28
|
-
const [showFollowUp, setShowFollowUp] = useState(false);
|
|
29
|
-
const [showForm, setShowForm] = useState(false);
|
|
30
|
-
const [submitted, setSubmitted] = useState(false);
|
|
31
|
-
const [startTime, setStartTime] = useState<number>(0);
|
|
32
|
-
const hasTrackedOpen = useRef(false);
|
|
33
|
-
const modalRef = useRef<HTMLDivElement>(null);
|
|
34
|
-
|
|
35
|
-
// Track calculator open (only once per modal opening)
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (isOpen && !hasTrackedOpen.current) {
|
|
38
|
-
trackCalculatorOpen(isBranchManager ? 'BM' : 'LO');
|
|
39
|
-
setStartTime(Date.now());
|
|
40
|
-
hasTrackedOpen.current = true;
|
|
41
|
-
} else if (!isOpen) {
|
|
42
|
-
// Reset tracking flag when modal closes
|
|
43
|
-
hasTrackedOpen.current = false;
|
|
44
|
-
}
|
|
45
|
-
}, [isOpen, isBranchManager, trackCalculatorOpen]);
|
|
46
|
-
|
|
47
|
-
// Update values when defaults change (for personalized pages)
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
setMonthlyVolume(defaultMonthlyVolume);
|
|
50
|
-
setCompensation(defaultCompensation);
|
|
51
|
-
}, [defaultMonthlyVolume, defaultCompensation]);
|
|
52
|
-
|
|
53
|
-
// Track slider interactions
|
|
54
|
-
const handleVolumeChange = (value: number) => {
|
|
55
|
-
setMonthlyVolume(value);
|
|
56
|
-
setHasInteracted(true);
|
|
57
|
-
trackCalculatorAdjustment();
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const handleCompensationChange = (value: number) => {
|
|
61
|
-
setCompensation(value);
|
|
62
|
-
setHasInteracted(true);
|
|
63
|
-
trackCalculatorAdjustment();
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Handle primary CTA click
|
|
67
|
-
const handlePrimaryCTA = () => {
|
|
68
|
-
setClickedPrimaryCTA(true);
|
|
69
|
-
setHasInteracted(true);
|
|
70
|
-
setShowForm(true);
|
|
71
|
-
trackAction('primary_cta_click', 'high');
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// Handle form submission
|
|
75
|
-
const handleFormSubmit = (data: any) => {
|
|
76
|
-
console.log('Form submitted:', data);
|
|
77
|
-
setSubmitted(true);
|
|
78
|
-
// Here you would send data to your backend/CRM
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// Handle close without clicking primary CTA
|
|
82
|
-
const handleClose = () => {
|
|
83
|
-
if (!showForm) {
|
|
84
|
-
onClose();
|
|
85
|
-
// If user interacted but didn't click primary CTA, show follow-up
|
|
86
|
-
if (hasInteracted && !clickedPrimaryCTA) {
|
|
87
|
-
setTimeout(() => setShowFollowUp(true), 300);
|
|
88
|
-
trackAction('close_without_primary_cta');
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
// Go back from form to calculator
|
|
92
|
-
setShowForm(false);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// Handle actual modal close
|
|
97
|
-
const handleActualClose = () => {
|
|
98
|
-
onClose();
|
|
99
|
-
setShowForm(false);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// Reset state when modal closes
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
if (!isOpen) {
|
|
105
|
-
// Reset after modal closes
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
setHasInteracted(false);
|
|
108
|
-
setClickedPrimaryCTA(false);
|
|
109
|
-
setShowForm(false);
|
|
110
|
-
}, 300);
|
|
111
|
-
}
|
|
112
|
-
}, [isOpen]);
|
|
113
|
-
|
|
114
|
-
// Calculations
|
|
115
|
-
const currentAnnualIncome = (monthlyVolume * 12 * compensation) / 10000;
|
|
116
|
-
const afnScenarioLow = currentAnnualIncome * 1.12;
|
|
117
|
-
const afnScenarioHigh = currentAnnualIncome * 1.22;
|
|
118
|
-
|
|
119
|
-
const formatCurrency = (value: number) => {
|
|
120
|
-
return new Intl.NumberFormat('en-US', {
|
|
121
|
-
style: 'currency',
|
|
122
|
-
currency: 'USD',
|
|
123
|
-
minimumFractionDigits: 0,
|
|
124
|
-
maximumFractionDigits: 0,
|
|
125
|
-
}).format(value);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const formatVolume = (value: number) => {
|
|
129
|
-
return new Intl.NumberFormat('en-US', {
|
|
130
|
-
style: 'currency',
|
|
131
|
-
currency: 'USD',
|
|
132
|
-
minimumFractionDigits: 0,
|
|
133
|
-
maximumFractionDigits: 0,
|
|
134
|
-
}).format(value);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
if (!isOpen) return null;
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<>
|
|
141
|
-
<AnimatePresence>
|
|
142
|
-
{isOpen && (
|
|
143
|
-
<>
|
|
144
|
-
{/* Backdrop */}
|
|
145
|
-
<motion.div
|
|
146
|
-
initial={{ opacity: 0 }}
|
|
147
|
-
animate={{ opacity: 1 }}
|
|
148
|
-
exit={{ opacity: 0 }}
|
|
149
|
-
onClick={onClose}
|
|
150
|
-
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50"
|
|
151
|
-
/>
|
|
152
|
-
|
|
153
|
-
{/* Modal */}
|
|
154
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 md:p-6 pointer-events-none">
|
|
155
|
-
<motion.div
|
|
156
|
-
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
157
|
-
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
158
|
-
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
159
|
-
transition={{ duration: 0.2 }}
|
|
160
|
-
role="dialog"
|
|
161
|
-
aria-modal="true"
|
|
162
|
-
className="w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-white dark:bg-zinc-900 rounded-2xl shadow-2xl pointer-events-auto"
|
|
163
|
-
ref={modalRef}
|
|
164
|
-
>
|
|
165
|
-
{/* Header */}
|
|
166
|
-
<div className="sticky top-0 bg-white dark:bg-zinc-900 border-b border-zinc-200 dark:border-zinc-800 px-6 py-4 flex items-start justify-between z-10">
|
|
167
|
-
<div>
|
|
168
|
-
<h2 className="text-2xl md:text-3xl text-zinc-900 dark:text-white">
|
|
169
|
-
Model Your Earnings Scenarios
|
|
170
|
-
</h2>
|
|
171
|
-
<p className="text-sm md:text-base text-zinc-600 dark:text-zinc-400 mt-1">
|
|
172
|
-
Adjust the assumptions below to see how changes in funded volume can impact income.
|
|
173
|
-
</p>
|
|
174
|
-
</div>
|
|
175
|
-
<button
|
|
176
|
-
onClick={onClose}
|
|
177
|
-
className="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors"
|
|
178
|
-
aria-label="Close"
|
|
179
|
-
>
|
|
180
|
-
<X className="w-5 h-5 text-zinc-500" />
|
|
181
|
-
</button>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
{/* Content */}
|
|
185
|
-
<div className="px-6 py-6 space-y-8">
|
|
186
|
-
{submitted ? (
|
|
187
|
-
// Success State
|
|
188
|
-
<div className="space-y-6 text-center py-8">
|
|
189
|
-
<div className="flex justify-center">
|
|
190
|
-
<CheckCircle className="w-16 h-16 text-green-500" />
|
|
191
|
-
</div>
|
|
192
|
-
<div className="space-y-2">
|
|
193
|
-
<h3 className="text-2xl text-zinc-900 dark:text-white">
|
|
194
|
-
Thanks — We'll Be In Touch
|
|
195
|
-
</h3>
|
|
196
|
-
<p className="text-base text-zinc-600 dark:text-zinc-400">
|
|
197
|
-
We'll follow up shortly to discuss your scenario.
|
|
198
|
-
</p>
|
|
199
|
-
<p className="text-sm text-zinc-500 dark:text-zinc-500">
|
|
200
|
-
In the meantime, feel free to keep exploring.
|
|
201
|
-
</p>
|
|
202
|
-
</div>
|
|
203
|
-
<button
|
|
204
|
-
onClick={onClose}
|
|
205
|
-
className="w-full px-6 py-3 bg-gradient-to-r from-[#4399D1] to-[#2B7AB8] hover:from-[#2B7AB8] hover:to-[#4399D1] text-white rounded-lg transition-all duration-300 shadow-lg"
|
|
206
|
-
>
|
|
207
|
-
Continue Exploring
|
|
208
|
-
</button>
|
|
209
|
-
</div>
|
|
210
|
-
) : showForm ? (
|
|
211
|
-
// Form State (HIGH INTENT)
|
|
212
|
-
<div className="space-y-4">
|
|
213
|
-
<p className="text-base text-zinc-600 dark:text-zinc-400">
|
|
214
|
-
{isBranchManager
|
|
215
|
-
? "We'd be happy to walk through your specific branch scenario and show how our AI tools can enhance your team's performance."
|
|
216
|
-
: "We'd be happy to walk through your specific scenario and show how our AI tools can enhance your production."}
|
|
217
|
-
</p>
|
|
218
|
-
<ProgressiveContactForm
|
|
219
|
-
onSubmit={handleFormSubmit}
|
|
220
|
-
onCancel={() => setShowForm(false)}
|
|
221
|
-
primaryCTA={isBranchManager ? "Review My Branch Scenario" : "Talk Through My Scenario"}
|
|
222
|
-
secondaryCTA="Go Back"
|
|
223
|
-
context={isBranchManager ? 'BM' : 'LO'}
|
|
224
|
-
/>
|
|
225
|
-
</div>
|
|
226
|
-
) : (
|
|
227
|
-
// Calculator State (DEFAULT)
|
|
228
|
-
<>
|
|
229
|
-
{/* Section 1: Baseline Inputs */}
|
|
230
|
-
<div>
|
|
231
|
-
<h3 className="text-lg md:text-xl text-zinc-900 dark:text-white mb-4">
|
|
232
|
-
Your Current Baseline
|
|
233
|
-
</h3>
|
|
234
|
-
|
|
235
|
-
{/* Monthly Funded Volume */}
|
|
236
|
-
<div className="mb-6">
|
|
237
|
-
<label className="block text-sm text-zinc-700 dark:text-zinc-300 mb-2">
|
|
238
|
-
Monthly Funded Volume
|
|
239
|
-
</label>
|
|
240
|
-
<input
|
|
241
|
-
type="range"
|
|
242
|
-
min={250000}
|
|
243
|
-
max={5000000}
|
|
244
|
-
step={50000}
|
|
245
|
-
value={monthlyVolume}
|
|
246
|
-
onChange={(e) => handleVolumeChange(Number(e.target.value))}
|
|
247
|
-
className="w-full h-2 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[#4399D1] [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-[#4399D1] [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer"
|
|
248
|
-
/>
|
|
249
|
-
<p className="text-xs text-zinc-500 dark:text-zinc-500 mt-2">
|
|
250
|
-
Slide to adjust if your actual production is higher or lower.
|
|
251
|
-
</p>
|
|
252
|
-
<p className="text-2xl text-zinc-900 dark:text-white mt-3">
|
|
253
|
-
{formatVolume(monthlyVolume)}
|
|
254
|
-
</p>
|
|
255
|
-
</div>
|
|
256
|
-
|
|
257
|
-
{/* Average Compensation */}
|
|
258
|
-
<div>
|
|
259
|
-
<label className="block text-sm text-zinc-700 dark:text-zinc-300 mb-2">
|
|
260
|
-
Your Average Compensation (basis points)
|
|
261
|
-
</label>
|
|
262
|
-
<input
|
|
263
|
-
type="range"
|
|
264
|
-
min={50}
|
|
265
|
-
max={250}
|
|
266
|
-
step={5}
|
|
267
|
-
value={compensation}
|
|
268
|
-
onChange={(e) => handleCompensationChange(Number(e.target.value))}
|
|
269
|
-
className="w-full h-2 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-[#4399D1] [&::-webkit-slider-thumb]:cursor-pointer [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-[#4399D1] [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:cursor-pointer"
|
|
270
|
-
/>
|
|
271
|
-
<p className="text-xs text-zinc-500 dark:text-zinc-500 mt-2">
|
|
272
|
-
Drag to adjust your average comp across all loans.
|
|
273
|
-
</p>
|
|
274
|
-
<p className="text-2xl text-zinc-900 dark:text-white mt-3">
|
|
275
|
-
{compensation} <span className="text-base text-zinc-500 dark:text-zinc-400">bps</span>
|
|
276
|
-
</p>
|
|
277
|
-
</div>
|
|
278
|
-
</div>
|
|
279
|
-
|
|
280
|
-
{/* Section 2: Current State Output */}
|
|
281
|
-
<div className="p-6 rounded-xl bg-gradient-to-br from-zinc-50 to-zinc-100 dark:from-zinc-800 dark:to-zinc-800/50 border border-zinc-200 dark:border-zinc-700">
|
|
282
|
-
<h3 className="text-lg md:text-xl text-zinc-900 dark:text-white mb-3">
|
|
283
|
-
Your Current Annual Income
|
|
284
|
-
</h3>
|
|
285
|
-
<div className="text-3xl md:text-4xl text-[#4399D1] mb-2">
|
|
286
|
-
{formatCurrency(currentAnnualIncome)}
|
|
287
|
-
</div>
|
|
288
|
-
<p className="text-sm text-zinc-600 dark:text-zinc-400">
|
|
289
|
-
Based on the inputs above.
|
|
290
|
-
</p>
|
|
291
|
-
</div>
|
|
292
|
-
|
|
293
|
-
{/* Section 3: AFN Scenario */}
|
|
294
|
-
<div className="p-6 rounded-xl bg-gradient-to-br from-[#4399D1]/5 to-[#7BBCE7]/5 border-2 border-[#4399D1]/30 dark:border-[#4399D1]/20">
|
|
295
|
-
<h3 className="text-lg md:text-xl text-zinc-900 dark:text-white mb-4">
|
|
296
|
-
AFN Scenario: Increased Funded Volume
|
|
297
|
-
</h3>
|
|
298
|
-
|
|
299
|
-
<div className="mb-4 p-3 bg-white/50 dark:bg-zinc-900/50 rounded-lg">
|
|
300
|
-
<p className="text-sm text-zinc-700 dark:text-zinc-300">
|
|
301
|
-
Modeled Volume Increase: <span className="text-[#4399D1]">+12% to +22%</span>
|
|
302
|
-
</p>
|
|
303
|
-
</div>
|
|
304
|
-
|
|
305
|
-
<div className="mb-4">
|
|
306
|
-
<p className="text-sm text-zinc-700 dark:text-zinc-300 mb-2">
|
|
307
|
-
Modeled Annual Income Range
|
|
308
|
-
</p>
|
|
309
|
-
<div className="text-2xl md:text-3xl text-[#4399D1]">
|
|
310
|
-
{formatCurrency(afnScenarioLow)} – {formatCurrency(afnScenarioHigh)}
|
|
311
|
-
</div>
|
|
312
|
-
</div>
|
|
313
|
-
|
|
314
|
-
<div className="p-4 bg-white/70 dark:bg-zinc-900/70 rounded-lg border border-[#4399D1]/20">
|
|
315
|
-
<p className="text-sm text-zinc-700 dark:text-zinc-300">
|
|
316
|
-
<span className="text-[#4399D1]">Important:</span> This model does not assume any change to your compensation. It illustrates how increased funded volume alone can impact earnings.
|
|
317
|
-
</p>
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
{/* Optional Summary Bar */}
|
|
322
|
-
<div className="p-4 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-700">
|
|
323
|
-
<p className="text-sm text-zinc-700 dark:text-zinc-300 text-center">
|
|
324
|
-
<span className="text-[#4399D1]">You control the assumptions.</span> AFN provides the system to improve outcomes.
|
|
325
|
-
</p>
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
{/* CTAs */}
|
|
329
|
-
<div className="space-y-3">
|
|
330
|
-
<button
|
|
331
|
-
onClick={handlePrimaryCTA}
|
|
332
|
-
className="w-full px-6 py-3 bg-gradient-to-r from-[#4399D1] to-[#2B7AB8] hover:from-[#2B7AB8] hover:to-[#4399D1] text-white rounded-lg transition-all duration-300 shadow-lg"
|
|
333
|
-
>
|
|
334
|
-
Talk Through My Scenario With AFN
|
|
335
|
-
</button>
|
|
336
|
-
<button
|
|
337
|
-
onClick={handleClose}
|
|
338
|
-
className="w-full px-6 py-3 bg-white dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 border border-zinc-300 dark:border-zinc-700 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors"
|
|
339
|
-
>
|
|
340
|
-
Close
|
|
341
|
-
</button>
|
|
342
|
-
</div>
|
|
343
|
-
|
|
344
|
-
{/* Data Safety Footer */}
|
|
345
|
-
<p className="text-xs text-zinc-400 dark:text-zinc-600 text-center">
|
|
346
|
-
These scenarios are illustrative only and do not represent a guarantee of earnings or compensation.
|
|
347
|
-
</p>
|
|
348
|
-
</>
|
|
349
|
-
)}
|
|
350
|
-
</div>
|
|
351
|
-
</motion.div>
|
|
352
|
-
</div>
|
|
353
|
-
</>
|
|
354
|
-
)}
|
|
355
|
-
</AnimatePresence>
|
|
356
|
-
|
|
357
|
-
{/* Follow-Up Modal */}
|
|
358
|
-
<CalculatorFollowUp
|
|
359
|
-
isOpen={showFollowUp}
|
|
360
|
-
onClose={() => setShowFollowUp(false)}
|
|
361
|
-
isBranchManager={isBranchManager}
|
|
362
|
-
clickedPrimaryCTA={clickedPrimaryCTA}
|
|
363
|
-
/>
|
|
364
|
-
</>
|
|
365
|
-
);
|
|
366
|
-
}
|