lakshmisravya123
Major upgrade: comprehensive negotiation coaching with EQ scoring and detailed reports
dad7400
import { useState, useRef, useEffect } from "react";
import { startNegotiation, sendResponse, acceptOffer } from "./utils/api";
function EQGauge({ label, value, max = 10 }) {
const pct = Math.round((value / max) * 100);
const color = pct >= 70 ? "#00b894" : pct >= 40 ? "#fdcb6e" : "#e17055";
return (
<div className="eq-gauge">
<div className="eq-gauge-label">{label}</div>
<div className="eq-gauge-bar-bg">
<div className="eq-gauge-bar-fill" style={{ width: pct + "%", background: color }} />
</div>
<div className="eq-gauge-value">{value}/{max}</div>
</div>
);
}
function MomentBadge({ rating }) {
const colors = { strong: "#00b894", neutral: "#fdcb6e", weak: "#e17055" };
return <span className="moment-badge" style={{ background: colors[rating] || "#555", color: "#000", padding: "2px 10px", borderRadius: "12px", fontSize: "0.75rem", fontWeight: 600, marginLeft: "8px" }}>{rating}</span>;
}
function CoachingPanel({ tip, warning, candidateTactics, momentRating }) {
if (!tip && !warning) return null;
return (
<div className="coaching-panel">
<div className="coaching-header">Real-Time Coach{momentRating && <MomentBadge rating={momentRating} />}</div>
{candidateTactics && candidateTactics.length > 0 && (
<div className="coaching-tactics">
<span className="coaching-tactics-label">Your tactics: </span>
{candidateTactics.map((t, i) => <span key={i} className="tactic-pill">{t}</span>)}
</div>
)}
{tip && <div className="coaching-tip"><strong>Try next:</strong> {tip}</div>}
{warning && warning.length > 0 && <div className="coaching-warning"><strong>Avoid:</strong> {warning}</div>}
</div>
);
}
function ScoreCircle({ score, label, size = 80 }) {
const r = (size - 10) / 2;
const circ = 2 * Math.PI * r;
const offset = circ - (score / 100) * circ;
const color = score >= 75 ? "#00b894" : score >= 50 ? "#fdcb6e" : "#e17055";
return (
<div className="score-circle" style={{ width: size, height: size, position: "relative", display: "inline-block" }}>
<svg width={size} height={size}>
<circle cx={size/2} cy={size/2} r={r} fill="none" stroke="#2d2d44" strokeWidth="5" />
<circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth="5"
strokeDasharray={circ} strokeDashoffset={offset} strokeLinecap="round"
transform={"rotate(-90 " + size/2 + " " + size/2 + ")"} />
</svg>
<div style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
<div style={{ fontSize: size > 100 ? "2rem" : "1.1rem", fontWeight: 700, color: "#fff" }}>{score}</div>
<div style={{ fontSize: "0.65rem", color: "#888" }}>{label}</div>
</div>
</div>
);
}
function ReportView({ report, onReset }) {
const r = report;
return (
<div className="app">
<h1><span>Performance Report</span></h1>
<div className="report">
<div className="report-top-grid">
<div className="report-main-score">
<ScoreCircle score={r.overallScore || 0} label="Overall" size={120} />
<div className="report-verdict">{r.verdict}</div>
{r.letterGrade && <div className="report-grade">Grade: {r.letterGrade}</div>}
</div>
<div className="report-score-cards">
<ScoreCircle score={r.emotionalIntelligence?.overall || 0} label="EQ" size={80} />
<ScoreCircle score={r.communicationScore?.overall || 0} label="Comms" size={80} />
<ScoreCircle score={(r.negotiationStyle?.effectiveness || 0) * 10} label="Style" size={80} />
</div>
</div>
<div className="salary-comparison">
<div className="salary-box"><div className="label">Target</div><div className="amount">{"$"}{(r.targetSalary||0).toLocaleString()}</div></div>
<div className="salary-box result-box"><div className="label">Final Offer</div><div className="amount">{"$"}{(r.finalSalary||0).toLocaleString()}</div><div className="pct">{r.percentOfTarget}% of target</div></div>
{r.marketContext && <div className="salary-box market-box"><div className="label">Market Range</div><div className="market-range">{"$"}{(r.marketContext.marketLow||0).toLocaleString()} - {"$"}{(r.marketContext.marketHigh||0).toLocaleString()}</div><div className="market-position">{r.marketContext.candidatePosition}</div></div>}
</div>
<p className="report-summary">{r.summary}</p>
{r.negotiationStyle && <div className="report-section style-section"><h3>Negotiation Style: <span className="style-name">{r.negotiationStyle.primary}</span></h3><p>{r.negotiationStyle.description}</p></div>}
{r.emotionalIntelligence && (
<div className="report-section eq-section">
<h3>Emotional Intelligence</h3>
<p className="eq-analysis">{r.emotionalIntelligence.analysis}</p>
<div className="eq-grid">
<EQGauge label="Empathy" value={r.emotionalIntelligence.empathy}/>
<EQGauge label="Assertiveness" value={r.emotionalIntelligence.assertiveness}/>
<EQGauge label="Composure" value={r.emotionalIntelligence.composure}/>
<EQGauge label="Rapport" value={r.emotionalIntelligence.rapport}/>
<EQGauge label="Adaptability" value={r.emotionalIntelligence.adaptability}/>
</div>
</div>
)}
{r.communicationScore && (
<div className="report-section">
<h3>Communication</h3>
<p className="eq-analysis">{r.communicationScore.analysis}</p>
<div className="eq-grid">
<EQGauge label="Clarity" value={r.communicationScore.clarity}/>
<EQGauge label="Persuasiveness" value={r.communicationScore.persuasiveness}/>
<EQGauge label="Active Listening" value={r.communicationScore.activeListening}/>
<EQGauge label="Question Quality" value={r.communicationScore.questionQuality}/>
</div>
</div>
)}
{r.powerDynamics && (
<div className="report-section power-section">
<h3>Power Dynamics</h3>
<div className="power-bars">
<div className="power-row"><span className="power-label">You: {r.powerDynamics.candidatePower}/10</span><div className="power-bar-bg"><div className="power-bar-you" style={{width:(r.powerDynamics.candidatePower*10)+"%"}}/></div></div>
<div className="power-row"><span className="power-label">Manager: {r.powerDynamics.managerPower}/10</span><div className="power-bar-bg"><div className="power-bar-mgr" style={{width:(r.powerDynamics.managerPower*10)+"%"}}/></div></div>
</div>
<p>{r.powerDynamics.assessment}</p>
{r.powerDynamics.shiftMoments?.map((m,i)=><div key={i} className="power-shift">{m}</div>)}
</div>
)}
{r.bestMoments?.length > 0 && (
<div className="report-section">
<h3>Best Moments</h3>
{r.bestMoments.map((m,i)=><div key={i} className="moment-card best"><div className="moment-round">Round {m.round}</div><div className="moment-quote">"{m.quote}"</div><div className="moment-why">{m.why}</div></div>)}
</div>
)}
{r.worstMoments?.length > 0 && (
<div className="report-section">
<h3>Weakest Moments</h3>
{r.worstMoments.map((m,i)=><div key={i} className="moment-card worst"><div className="moment-round">Round {m.round}</div><div className="moment-quote">"{m.quote}"</div><div className="moment-why">{m.why}</div></div>)}
</div>
)}
{r.tacticsUsed?.length > 0 && (
<div className="report-section">
<h3>Tactics Analysis</h3>
<div className="tactics-grid">
{r.tacticsUsed.map((t,i)=><div key={i} className={"tactic-card "+t.effectiveness}><div className="tactic-card-name">{t.name}</div><div className="tactic-card-eff">{t.effectiveness}</div><div className="tactic-card-example">"{t.example}"</div></div>)}
</div>
</div>
)}
{r.missedOpportunities?.length > 0 && (
<div className="report-section">
<h3>Missed Opportunities</h3>
{r.missedOpportunities.map((m,i)=><div key={i} className="missed-card"><div className="missed-situation"><strong>Situation:</strong> {m.situation}</div><div className="missed-better"><strong>Better approach:</strong> {m.betterApproach}</div><div className="missed-impact"><strong>Impact:</strong> {m.impact}</div></div>)}
</div>
)}
{r.marketContext && <div className="report-section"><h3>Market Context</h3><p>{r.marketContext.analysis}</p></div>}
<div className="report-two-col">
<div className="report-section"><h3>Strengths</h3><ul>{r.strengths?.map((s,i)=><li key={i}>{s}</li>)}</ul></div>
<div className="report-section"><h3>Areas to Improve</h3><ul>{r.improvements?.map((s,i)=><li key={i}>{s}</li>)}</ul></div>
</div>
{r.personalizedTips?.length > 0 && (
<div className="report-section">
<h3>Personalized Pro Tips</h3>
<div className="tips-grid">
{r.personalizedTips.map((t,i)=><div key={i} className="tip-card"><div className="tip-category">{t.category}</div><div className="tip-text">{t.tip}</div></div>)}
</div>
</div>
)}
{r.nextSessionFocus && <div className="report-section next-focus"><h3>Next Session Focus</h3><p>{r.nextSessionFocus}</p></div>}
<button className="btn btn-primary" onClick={onReset}>Practice Again</button>
</div>
</div>
);
}
export default function App() {
const [phase, setPhase] = useState("setup");
const [config, setConfig] = useState({ role:"", company:"", currentSalary:"", targetSalary:"", difficulty:"medium", scenarioType:"salary" });
const [sessionId, setSessionId] = useState(null);
const [messages, setMessages] = useState([]);
const [currentOffer, setCurrentOffer] = useState(0);
const [response, setResponse] = useState("");
const [loading, setLoading] = useState(false);
const [report, setReport] = useState(null);
const [round, setRound] = useState(1);
const [tactic, setTactic] = useState("");
const [tacticExplanation, setTacticExplanation] = useState("");
const [error, setError] = useState("");
const [coaching, setCoaching] = useState(null);
const [eqScores, setEqScores] = useState(null);
const [scenarioLabel, setScenarioLabel] = useState("");
const chatRef = useRef(null);
const inputRef = useRef(null);
useEffect(() => { if (chatRef.current) chatRef.current.scrollTop = chatRef.current.scrollHeight; }, [messages, coaching]);
useEffect(() => { if (phase === "negotiation" && !loading && inputRef.current) inputRef.current.focus(); }, [phase, loading, messages]);
const handleStart = async () => {
if (!config.role || !config.targetSalary) { setError("Role and target salary are required"); return; }
setLoading(true); setError("");
try {
const data = await startNegotiation({ ...config, targetSalary: Number(config.targetSalary), currentSalary: Number(config.currentSalary) || undefined });
setSessionId(data.sessionId); setCurrentOffer(data.initialOffer); setScenarioLabel(data.scenarioLabel || "Salary Negotiation");
setMessages([{ role: "Hiring Manager", text: data.openingStatement + " Our initial offer is " + "$" + (data.initialOffer||0).toLocaleString() + ".", name: data.hiringManagerName }]);
setPhase("negotiation");
} catch (err) { setError(err.message); } finally { setLoading(false); }
};
const handleRespond = async () => {
if (!response.trim()) return;
setMessages(prev => [...prev, { role: "You", text: response }]);
const myResponse = response; setResponse(""); setLoading(true); setCoaching(null);
try {
const data = await sendResponse(sessionId, myResponse);
setCurrentOffer(data.currentOffer);
setMessages(prev => [...prev, { role: "Hiring Manager", text: data.response }]);
setRound(data.round);
if (data.tactic) setTactic(data.tactic);
if (data.tacticExplanation) setTacticExplanation(data.tacticExplanation);
if (data.emotionalIntelligence) setEqScores(data.emotionalIntelligence);
setCoaching({ tip: data.coachingTip, warning: data.coachingWarning, candidateTactics: data.candidateTactics, momentRating: data.momentRating });
} catch (err) { setError(err.message); } finally { setLoading(false); }
};
const handleAccept = async () => {
setLoading(true);
try { const data = await acceptOffer(sessionId); setReport(data); setPhase("report"); }
catch (err) { setError(err.message); } finally { setLoading(false); }
};
const resetAll = () => { setPhase("setup"); setMessages([]); setReport(null); setRound(1); setCoaching(null); setEqScores(null); setTactic(""); setTacticExplanation(""); setError(""); };
if (phase === "report" && report) return <ReportView report={report} onReset={resetAll} />;
if (phase === "negotiation") {
return (
<div className="app">
<div className="neg-header"><h1><span>Negotiation Simulator</span></h1><div className="scenario-tag">{scenarioLabel}</div></div>
<div className="offer-display">
<div className="offer-meta">Round {round} | {config.difficulty} difficulty</div>
<div className="offer-amount">{"$"}{(currentOffer||0).toLocaleString()}</div>
<div className="offer-meta">Target: {"$"}{Number(config.targetSalary).toLocaleString()}</div>
<div className="offer-progress-bar"><div className="offer-progress-fill" style={{ width: Math.min(100, Math.round((currentOffer / Number(config.targetSalary)) * 100)) + "%" }} /></div>
<div className="offer-pct">{Math.round((currentOffer / Number(config.targetSalary)) * 100)}% of target</div>
{tactic && <div className="tactic-badge-wrapper"><span className="tactic-badge">{tactic}</span>{tacticExplanation && <span className="tactic-explanation">{tacticExplanation}</span>}</div>}
</div>
{eqScores && <div className="eq-mini-dashboard"><div className="eq-mini-item"><span className="eq-mini-label">Empathy</span><span className="eq-mini-val">{eqScores.empathy}</span></div><div className="eq-mini-item"><span className="eq-mini-label">Assertive</span><span className="eq-mini-val">{eqScores.assertiveness}</span></div><div className="eq-mini-item"><span className="eq-mini-label">Composure</span><span className="eq-mini-val">{eqScores.composure}</span></div><div className="eq-mini-item"><span className="eq-mini-label">Rapport</span><span className="eq-mini-val">{eqScores.rapport}</span></div></div>}
<div className="chat-container" ref={chatRef}>
{messages.map((m,i) => <div key={i} className={"message " + (m.role==="You" ? "candidate" : "manager")}><div className="role">{m.role}{m.name ? " ("+m.name+")" : ""}</div><div className="text">{m.text}</div></div>)}
{loading && <div className="loading"><div className="spinner"></div>Thinking...</div>}
</div>
{coaching && <CoachingPanel tip={coaching.tip} warning={coaching.warning} candidateTactics={coaching.candidateTactics} momentRating={coaching.momentRating} />}
{error && <p className="error-text">{error}</p>}
<div className="response-area">
<textarea ref={inputRef} placeholder="Your response... (Enter to send, Shift+Enter for new line)" value={response} onChange={e => setResponse(e.target.value)} onKeyDown={e => { if (e.key==="Enter" && !e.shiftKey) { e.preventDefault(); handleRespond(); }}} disabled={loading} />
<button className="btn btn-primary btn-send" onClick={handleRespond} disabled={loading || !response.trim()}>Send</button>
</div>
<div className="action-row">
<button className="btn btn-accept" onClick={handleAccept} disabled={loading}>Accept Offer ({"$"}{(currentOffer||0).toLocaleString()})</button>
<button className="btn-walk" onClick={handleAccept} disabled={loading}>Walk Away</button>
</div>
</div>
);
}
return (
<div className="app">
<h1><span>Negotiation Simulator</span></h1>
<p className="subtitle">Practice real-world negotiations with AI coaching. Get detailed performance analysis with emotional intelligence scoring.</p>
{error && <p className="error-text">{error}</p>}
<div className="setup-form">
<div className="input-group"><label>Scenario Type</label>
<div className="scenario-selector">
{[{id:"salary",label:"Salary",desc:"Negotiate compensation"},{id:"promotion",label:"Promotion",desc:"Ask for a raise"},{id:"resources",label:"Resources",desc:"Project budget"},{id:"remote",label:"Remote Work",desc:"Flexibility terms"}].map(s =>
<button key={s.id} className={"scenario-btn "+(config.scenarioType===s.id?"active":"")} onClick={()=>setConfig({...config,scenarioType:s.id})}>
<span className="scenario-label">{s.label}</span>
<span className="scenario-desc">{s.desc}</span>
</button>
)}
</div>
</div>
<div className="form-row">
<div className="input-group"><label>Job Role *</label><input placeholder="e.g., Senior Software Engineer" value={config.role} onChange={e=>setConfig({...config,role:e.target.value})}/></div>
<div className="input-group"><label>Company (optional)</label><input placeholder="e.g., Google" value={config.company} onChange={e=>setConfig({...config,company:e.target.value})}/></div>
</div>
<div className="form-row">
<div className="input-group"><label>Current Salary</label><input type="number" placeholder="e.g., 120000" value={config.currentSalary} onChange={e=>setConfig({...config,currentSalary:e.target.value})}/></div>
<div className="input-group"><label>Target Salary *</label><input type="number" placeholder="e.g., 150000" value={config.targetSalary} onChange={e=>setConfig({...config,targetSalary:e.target.value})}/></div>
</div>
<div className="input-group"><label>Difficulty</label>
<div className="difficulty-selector">
{[{id:"easy",label:"Easy",desc:"Flexible manager"},{id:"medium",label:"Medium",desc:"Standard tactics"},{id:"hard",label:"Hard",desc:"Tough negotiator"}].map(d =>
<button key={d.id} className={"diff-btn "+(config.difficulty===d.id?"active":"")+" diff-"+d.id} onClick={()=>setConfig({...config,difficulty:d.id})}>
<span className="diff-label">{d.label}</span>
<span className="diff-desc">{d.desc}</span>
</button>
)}
</div>
</div>
<button className="btn btn-primary" onClick={handleStart} disabled={loading}>{loading ? "Starting..." : "Start Negotiation"}</button>
</div>
<div className="features-grid">
<div className="feature-card"><div className="feature-title">EQ Scoring</div><div className="feature-desc">Real-time emotional intelligence feedback</div></div>
<div className="feature-card"><div className="feature-title">Live Coaching</div><div className="feature-desc">Tips after each round on what to say next</div></div>
<div className="feature-card"><div className="feature-title">Deep Analytics</div><div className="feature-desc">Style analysis, power dynamics, market context</div></div>
<div className="feature-card"><div className="feature-title">4 Scenarios</div><div className="feature-desc">Salary, promotion, resources, remote work</div></div>
</div>
</div>
);
}