/* salary.jsx — AI salary benchmarker by country */ const SALARY_COUNTRIES = [ { code:"US", label:"United States", currency:"USD", symbol:"$" }, { code:"GB", label:"United Kingdom", currency:"GBP", symbol:"£" }, { code:"CA", label:"Canada", currency:"CAD", symbol:"CA$"}, { code:"AU", label:"Australia", currency:"AUD", symbol:"A$" }, { code:"DE", label:"Germany", currency:"EUR", symbol:"€" }, { code:"FR", label:"France", currency:"EUR", symbol:"€" }, { code:"NL", label:"Netherlands", currency:"EUR", symbol:"€" }, { code:"SG", label:"Singapore", currency:"SGD", symbol:"S$" }, { code:"AE", label:"UAE", currency:"AED", symbol:"AED"}, { code:"IN", label:"India", currency:"INR", symbol:"₹" }, { code:"BD", label:"Bangladesh", currency:"BDT", symbol:"৳" }, { code:"PK", label:"Pakistan", currency:"PKR", symbol:"PKR"}, { code:"MY", label:"Malaysia", currency:"MYR", symbol:"RM" }, { code:"NG", label:"Nigeria", currency:"NGN", symbol:"₦" }, { code:"ZA", label:"South Africa", currency:"ZAR", symbol:"R" }, { code:"BR", label:"Brazil", currency:"BRL", symbol:"R$" }, { code:"MX", label:"Mexico", currency:"MXN", symbol:"MX$"}, { code:"PH", label:"Philippines", currency:"PHP", symbol:"₱" }, { code:"JP", label:"Japan", currency:"JPY", symbol:"¥" }, { code:"KR", label:"South Korea", currency:"KRW", symbol:"₩" }, { code:"SA", label:"Saudi Arabia", currency:"SAR", symbol:"SR" }, { code:"EG", label:"Egypt", currency:"EGP", symbol:"E£" }, { code:"KE", label:"Kenya", currency:"KES", symbol:"KSh"}, { code:"GH", label:"Ghana", currency:"GHS", symbol:"GH₵"}, { code:"TR", label:"Turkey", currency:"TRY", symbol:"₺" }, { code:"ID", label:"Indonesia", currency:"IDR", symbol:"Rp" }, { code:"TH", label:"Thailand", currency:"THB", symbol:"฿" }, { code:"VN", label:"Vietnam", currency:"VND", symbol:"₫" }, ]; function SalaryDrawer({ data, onClose }) { const { useState: S, useEffect: E, useMemo: M } = React; const [country, setCountry] = S("US"); const [stage, setStage] = S("idle"); const [result, setResult] = S(null); const [errMsg, setErrMsg] = S(""); const [limitHit, setLimitHit] = S(false); E(() => { // Auto-detect country from location const loc = (data.basics?.location || "").toLowerCase(); const detected = SALARY_COUNTRIES.find(c => loc.includes(c.label.toLowerCase()) || loc.includes(c.code.toLowerCase()) || loc.includes(c.currency.toLowerCase()) ); if (detected) setCountry(detected.code); }, [data.basics?.location]); E(() => { const onKey = e => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, []); // Estimate years of experience from experience array const yearsExp = M(() => { const exp = data.experience || []; if (!exp.length) return null; let totalMonths = 0; exp.forEach(x => { const start = x.start ? new Date(x.start) : null; const end = x.end && x.end !== "Present" ? new Date(x.end) : new Date(); if (start && !isNaN(start)) { totalMonths += Math.max(0, (end - start) / (1000 * 60 * 60 * 24 * 30.4)); } }); return totalMonths > 0 ? Math.round(totalMonths / 12) : null; }, [data.experience]); const selectedCountry = SALARY_COUNTRIES.find(c => c.code === country); const estimate = async () => { if (!window.groqStream) { setErrMsg("AI not available."); setStage("error"); return; } setStage("loading"); setResult(null); setErrMsg(""); const role = data.basics?.title || "Professional"; const location = data.basics?.location || selectedCountry?.label || ""; const years = yearsExp !== null ? yearsExp : "unknown"; const skills = (data.skills || []).flatMap(g => g.items || []).slice(0, 10).join(", "); const countryLabel = selectedCountry?.label || country; const currency = selectedCountry?.currency || "USD"; const symbol = selectedCountry?.symbol || "$"; let raw = ""; await window.groqStream({ messages: [ { role: "system", content: "You are a compensation expert with deep knowledge of global salary data. Provide realistic, research-based salary estimates." }, { role: "user", content: `Estimate annual salary range for this professional in ${countryLabel} (${currency}). Role: ${role} Location: ${location || countryLabel} Years of experience: ${years} Key skills: ${skills} Return ONLY a JSON object with this exact structure: { "min": 85000, "mid": 105000, "max": 130000, "currency": "${currency}", "symbol": "${symbol}", "percentile25": 80000, "percentile75": 125000, "insight": "One sentence on market context — demand, trends, or what drives range variance for this role in ${countryLabel}.", "topPayingCities": ["City1", "City2", "City3"] } Use realistic annual gross salary in ${currency}. All values must be numbers (no commas or currency symbols in numbers).` }, ], maxTokens: 400, onChunk: (_, full) => { raw = full; }, onDone: () => { try { let s = raw.trim().replace(/^```json\s*/i,"").replace(/^```\s*/i,"").replace(/\s*```$/i,""); const obj = JSON.parse(s.match(/\{[\s\S]*\}/)?.[0] || s); setResult(obj); setStage("done"); } catch(_) { setErrMsg("Could not parse result. Try again."); setStage("error"); } }, onError: e => { if (e === "free_limit_reached") { setLimitHit(true); } else { setErrMsg(typeof e==="string"?e:"AI error."); } setStage("error"); }, }); }; const fmt = n => { if (!n) return "—"; const sym = result?.symbol || selectedCountry?.symbol || "$"; if (n >= 1_000_000) return sym + (n/1_000_000).toFixed(1) + "M"; if (n >= 1_000) return sym + Math.round(n/1000) + "K"; return sym + n.toLocaleString(); }; const pct = result ? Math.round(((result.mid - result.min) / (result.max - result.min)) * 100) : 50; return ( <>
AI-estimated market range for your role
Fetching market data…
{errMsg}
AI estimates based on market data. Actual salaries vary by employer, sector, and negotiation.