Spaces:
Running
Running
File size: 15,033 Bytes
7dff677 778ab74 7dff677 aa3c3cf 7dff677 aa3c3cf 7dff677 aa3c3cf 7dff677 aa3c3cf 7dff677 aa3c3cf 7dff677 aa3c3cf 778ab74 7dff677 778ab74 7dff677 778ab74 7dff677 aa3c3cf 7dff677 aa3c3cf 7dff677 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | import React, { forwardRef } from 'react'
export interface ReportProps {
form: {
selected_name: string
current_club: string
interested_club: string
contract_years: number
age: number
injuries_24m: number
asking_price: number
market_value_estimation: number
}
result: {
ledger: {
intrinsic_performance_value: number
category: string
depreciation: number
baseline_value: number
external_multiplier: number
hard_cap: number
}
nlp_results?: {
durability: number
recency: number
agent: number
}
nlp_found?: boolean
}
}
export const ReportTemplate = forwardRef<HTMLDivElement, ReportProps>(({ form, result }, ref) => {
const L = result.ledger
const isOverpay = form.asking_price > L.hard_cap
const dateStr = new Date().toLocaleString('en-US', { dateStyle: 'long', timeStyle: 'short' })
return (
<div
ref={ref}
className="printable-report"
style={{
backgroundColor: '#ffffff',
color: '#0f172a',
fontFamily: "'Inter', sans-serif",
boxSizing: 'border-box',
display: 'none', // Hidden on screen, shown in print via CSS
}}
>
{/* HEADER */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', borderBottom: '2px solid #e2e8f0', paddingBottom: '24px', marginBottom: '32px' }}>
<div>
<h1 style={{ fontSize: '32px', fontWeight: 800, margin: 0, color: '#020617', letterSpacing: '-0.03em' }}>
FairValue Strategic Report
</h1>
<p style={{ fontSize: '14px', color: '#64748b', marginTop: '4px' }}>
AI-Driven Transfer Valuation Intelligence
</p>
</div>
<div style={{ textAlign: 'right', fontSize: '12px', color: '#475569', lineHeight: 1.6 }}>
<div style={{ fontWeight: 600, color: '#0f172a' }}>Lawrence Oladeji</div>
<div>oladeji.lawrence@gmail.com</div>
<div><a href="https://wa.me/2349038819790" style={{ color: '#0f172a', textDecoration: 'none' }}>WhatsApp</a></div>
<div><a href="https://premiership-player-fair-value.vercel.app/" style={{ color: '#2563eb', textDecoration: 'none' }}>Website</a></div>
<div style={{ marginTop: '8px', color: '#94a3b8' }}>Generated: {dateStr}</div>
</div>
</div>
{/* PLAYER PROFILE */}
<div style={{ marginBottom: '32px', pageBreakInside: 'avoid', breakInside: 'avoid' }}>
<h2 style={{ fontSize: '18px', fontWeight: 700, color: '#334155', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '16px' }}>
Player Profile & Parameters
</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', background: '#f8fafc', padding: '24px', borderRadius: '12px', border: '1px solid #e2e8f0' }}>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Target Player</div>
<div style={{ fontSize: '20px', fontWeight: 700, color: '#0f172a' }}>{form.selected_name || 'N/A'}</div>
</div>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Transfer Direction</div>
<div style={{ fontSize: '16px', fontWeight: 600, color: '#0f172a' }}>
{form.current_club || 'Unknown'} → {form.interested_club || 'Unknown'}
</div>
</div>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Age</div>
<div style={{ fontSize: '16px', fontWeight: 600, color: '#0f172a' }}>{form.age} years</div>
</div>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Contract Remaining</div>
<div style={{ fontSize: '16px', fontWeight: 600, color: '#0f172a' }}>{form.contract_years} years</div>
</div>
</div>
</div>
{/* FINANCIAL VALUATION LEDGER */}
<div style={{ marginBottom: '32px', pageBreakInside: 'avoid', breakInside: 'avoid' }}>
<h2 style={{ fontSize: '18px', fontWeight: 700, color: '#334155', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '16px' }}>
AI Valuation Ledger
</h2>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '14px' }}>
<colgroup>
<col style={{ width: '70%' }} />
<col style={{ width: '30%' }} />
</colgroup>
<tbody>
<tr style={{ borderBottom: '1px solid #e2e8f0' }}>
<td style={{ padding: '12px 0', color: '#475569', fontWeight: 500 }}>Intrinsic Performance Value</td>
<td style={{ padding: '12px 0', textAlign: 'right', fontWeight: 700, color: '#0f172a' }}>£{L.intrinsic_performance_value.toFixed(1)}m</td>
</tr>
<tr style={{ borderBottom: '1px solid #e2e8f0' }}>
<td style={{ padding: '12px 0', color: '#475569', fontWeight: 500 }}>
Age & Contract Impact (SHAP) — {L.depreciation > 0 ? '📉 Depreciation' : '📈 Appreciation'}
{L.depreciation > 0 && form.age <= 23 && (
<div style={{ fontSize: '11px', color: '#94a3b8', marginTop: '2px' }}>
* Model reflects current output, not future potential
</div>
)}
</td>
<td style={{ padding: '12px 0', textAlign: 'right', fontWeight: 700, color: L.depreciation > 0 ? '#ef4444' : '#22c55e' }}>
{L.depreciation > 0 ? '-' : '+'}£{Math.abs(L.depreciation).toFixed(1)}m
</td>
</tr>
<tr style={{ borderBottom: '1px solid #e2e8f0' }}>
<td style={{ padding: '12px 0', color: '#475569', fontWeight: 500 }}>ML Baseline Value</td>
<td style={{ padding: '12px 0', textAlign: 'right', fontWeight: 700, color: '#0f172a' }}>£{L.baseline_value.toFixed(1)}m</td>
</tr>
<tr style={{ borderBottom: '1px solid #e2e8f0' }}>
<td style={{ padding: '12px 0', color: '#475569', fontWeight: 500 }}>Live NLP Market Multiplier</td>
<td style={{ padding: '12px 0', textAlign: 'right', fontWeight: 700, color: L.external_multiplier > 1 ? '#22c55e' : '#ef4444' }}>
×{L.external_multiplier.toFixed(3)}
</td>
</tr>
<tr style={{ backgroundColor: '#f0fdf4', border: '1px solid #bbf7d0' }}>
<td style={{ padding: '16px 12px', color: '#166534', fontWeight: 700, fontSize: '16px' }}>Calculated Fair Value (Ceiling)</td>
<td style={{ padding: '16px 12px', textAlign: 'right', fontWeight: 800, fontSize: '20px', color: '#16a34a' }}>
£{L.hard_cap.toFixed(1)}m
</td>
</tr>
<tr style={{ backgroundColor: '#fefce8', border: '1px solid #fde68a' }}>
<td style={{ padding: '12px 12px', color: '#92400e', fontWeight: 600, fontSize: '13px' }}>
🎯 Recommended Opening Bid
</td>
<td style={{ padding: '12px 12px', textAlign: 'right', fontWeight: 700, fontSize: '15px', color: '#b45309' }}>
£{(L.hard_cap * 0.85).toFixed(1)}m
</td>
</tr>
<tr style={{ backgroundColor: '#fff7ed', border: '1px solid #fed7aa' }}>
<td style={{ padding: '12px 12px', color: '#7c2d12', fontWeight: 600, fontSize: '13px' }}>
⚠️ Walk-Away Ceiling
</td>
<td style={{ padding: '12px 12px', textAlign: 'right', fontWeight: 700, fontSize: '15px', color: '#dc2626' }}>
£{L.hard_cap.toFixed(1)}m
</td>
</tr>
</tbody>
</table>
</div>
{/* NLP INTELLIGENCE BREAKDOWN */}
{result.nlp_results && (
<div style={{ marginBottom: '32px', pageBreakInside: 'avoid', breakInside: 'avoid' }}>
<h2 style={{ fontSize: '18px', fontWeight: 700, color: '#334155', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '16px' }}>
Live Market Intelligence (NLP Breakdown)
</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '16px', background: '#f8fafc', padding: '24px', borderRadius: '12px', border: '1px solid #e2e8f0' }}>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Recent Form & Impact</div>
<div style={{ fontSize: '18px', fontWeight: 700, color: (result.nlp_results?.recency ?? 0) === 0 ? (result.nlp_found ? '#475569' : '#94a3b8') : (result.nlp_results?.recency ?? 0) < 0 ? '#ef4444' : '#22c55e' }}>
{(result.nlp_results?.recency ?? 0) === 0 ? (result.nlp_found ? 'Neutral / Factual' : 'No signals detected') : `${(result.nlp_results?.recency ?? 0) > 0 ? '+' : ''}${(result.nlp_results?.recency ?? 0).toFixed(2)}`}
</div>
</div>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Injury / Availability</div>
<div style={{ fontSize: '18px', fontWeight: 700, color: (result.nlp_results?.durability ?? 0) === 0 ? (result.nlp_found ? '#475569' : '#94a3b8') : (result.nlp_results?.durability ?? 0) < 0 ? '#ef4444' : '#0f172a' }}>
{(result.nlp_results?.durability ?? 0) === 0 ? (result.nlp_found ? 'Neutral / Factual' : 'No signals detected') : `${(result.nlp_results?.durability ?? 0) > 0 ? '+' : ''}${(result.nlp_results?.durability ?? 0).toFixed(2)}`}
</div>
</div>
<div>
<div style={{ fontSize: '11px', color: '#64748b', textTransform: 'uppercase' }}>Transfer Speculation</div>
<div style={{ fontSize: '18px', fontWeight: 700, color: (result.nlp_results?.agent ?? 0) === 0 ? (result.nlp_found ? '#475569' : '#94a3b8') : (result.nlp_results?.agent ?? 0) < 0 ? '#ef4444' : '#22c55e' }}>
{(result.nlp_results?.agent ?? 0) === 0 ? (result.nlp_found ? 'Neutral / Factual' : 'No signals detected') : `${(result.nlp_results?.agent ?? 0) > 0 ? '+' : ''}${(result.nlp_results?.agent ?? 0).toFixed(2)}`}
</div>
</div>
</div>
<div style={{ fontSize: '13px', color: '#64748b', marginTop: '12px', lineHeight: 1.5 }}>
<strong>How this works:</strong> The Live NLP Market Multiplier (currently <strong>×{L.external_multiplier.toFixed(3)}</strong>) is calculated from the sentiment scores above. A multiplier of exactly 1.000 means perfectly neutral market hype. A score above 1.0 indicates high demand and positive news, allowing the selling club to charge a premium. A score below 1.0 indicates negative press (e.g. poor form or injury history), creating leverage to negotiate a discount.
</div>
</div>
)}
{/* VERDICT & SUMMARY */}
<div style={{ marginBottom: '40px', pageBreakBefore: 'always', breakBefore: 'page' }}>
<h2 style={{ fontSize: '18px', fontWeight: 700, color: '#334155', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '16px' }}>
Executive Summary & Verdict
</h2>
<div style={{ padding: '24px', background: isOverpay ? '#fef2f2' : '#f0fdf4', border: `1px solid ${isOverpay ? '#fecaca' : '#bbf7d0'}`, borderRadius: '12px' }}>
<p style={{ fontSize: '15px', color: isOverpay ? '#991b1b' : '#166534', lineHeight: 1.6, marginBottom: '16px' }}>
<strong>Negotiation Intel:</strong> Based on the real-time evaluation engine, {form.selected_name}'s intrinsic value sits at £{L.intrinsic_performance_value.toFixed(1)}m.
Age & contract dynamics apply a {L.depreciation > 0 ? `depreciation of -£${Math.abs(L.depreciation).toFixed(1)}m` : `appreciation of +£${Math.abs(L.depreciation).toFixed(1)}m`},
with live NLP market sentiment at ×{L.external_multiplier.toFixed(3)}. The absolute financial ceiling (Fair Value) is <strong>£{L.hard_cap.toFixed(1)}m</strong>.
<br/><br/>
<strong>Validation:</strong> {(result.nlp_results?.recency ?? 0) > 0 ? "Player's recent form commands a market premium." : "Recent form is suboptimal — leverage this to negotiate a lower fee."} {L.depreciation > 0 ? `Age & contract profile adds £${Math.abs(L.depreciation).toFixed(1)}m in depreciation risk — use this as a negotiation lever.` : `Young age and contract security add £${Math.abs(L.depreciation).toFixed(1)}m in appreciation value, justifying a strong offer.`}
{L.depreciation > 0 && form.age <= 23 && (
<span style={{ display: 'block', marginTop: '8px', fontSize: '12px', color: '#64748b' }}>
ℹ️ Note: Depreciation reflects the player's current statistical output stage vs. their peak. Young players may command a future-value premium not captured in this model.
</span>
)}
</p>
<div style={{ fontSize: '18px', fontWeight: 800, color: isOverpay ? '#dc2626' : '#16a34a', borderTop: `1px solid ${isOverpay ? '#fca5a5' : '#86efac'}`, paddingTop: '16px' }}>
VERDICT: {isOverpay ? 'OVERPAY RISK' : 'FAIR DEAL - PROCEED WITH CONFIDENCE'}
</div>
<div style={{ fontSize: '14px', color: isOverpay ? '#b91c1c' : '#15803d', marginTop: '6px' }}>
The selling club's asking price of £{form.asking_price}m {isOverpay ? 'exceeds' : 'is safely within'} our calculated Fair Value.
</div>
</div>
</div>
{/* DEFINITION OF TERMS */}
<div style={{ borderTop: '2px solid #e2e8f0', paddingTop: '24px', pageBreakInside: 'avoid', breakInside: 'avoid' }}>
<h2 style={{ fontSize: '14px', fontWeight: 700, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '12px' }}>
Definition of Terms
</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', fontSize: '12px', color: '#475569', lineHeight: 1.5 }}>
<div>
<strong style={{ color: '#0f172a' }}>Intrinsic Performance Value:</strong> Player's raw talent value derived purely from on-pitch statistics.
</div>
<div>
<strong style={{ color: '#0f172a' }}>Age & Contract Impact:</strong> Financial adjustment based on age and remaining contract years.
</div>
<div>
<strong style={{ color: '#0f172a' }}>NLP Multiplier:</strong> Live web-scraping adjustment factor. Values {'>'} 1.0 indicate positive hype (premium), while values {'<'} 1.0 indicate negative press (discount).
</div>
<div>
<strong style={{ color: '#0f172a' }}>Fair Value:</strong> The absolute maximum recommended price (financial ceiling).
</div>
</div>
</div>
</div>
)
})
|