Spaces:
Sleeping
Sleeping
| import { | |
| Chart as ChartJS, | |
| ArcElement, | |
| Tooltip, | |
| Legend, | |
| CategoryScale, | |
| LinearScale, | |
| BarElement, | |
| Title, | |
| RadialLinearScale, | |
| PointElement, | |
| LineElement, | |
| Filler, | |
| } from 'chart.js'; | |
| import { Doughnut, Bar, Radar } from 'react-chartjs-2'; | |
| ChartJS.register( | |
| ArcElement, | |
| Tooltip, | |
| Legend, | |
| CategoryScale, | |
| LinearScale, | |
| BarElement, | |
| Title, | |
| RadialLinearScale, | |
| PointElement, | |
| LineElement, | |
| Filler | |
| ); | |
| export function DTIGauge({ dti }) { | |
| const data = { | |
| labels: ['DTI', 'Remaining Capacity'], | |
| datasets: [ | |
| { | |
| data: [dti, Math.max(0, 100 - dti)], | |
| backgroundColor: [ | |
| dti > 50 ? '#ef4444' : dti > 30 ? '#f59e0b' : '#10b981', | |
| '#f3f4f6' | |
| ], | |
| borderWidth: 0, | |
| cutout: '85%' | |
| } | |
| ] | |
| }; | |
| const options = { | |
| rotation: -90, | |
| circumference: 180, | |
| plugins: { | |
| legend: { display: false }, | |
| tooltip: { enabled: false } | |
| }, | |
| maintainAspectRatio: true, | |
| }; | |
| return ( | |
| <div style={{ position: 'relative', width: '220px', margin: '0 auto' }}> | |
| <Doughnut data={data} options={options} /> | |
| <div style={{ | |
| position: 'absolute', | |
| top: '70%', | |
| left: '50%', | |
| transform: 'translate(-50%, -50%)', | |
| textAlign: 'center' | |
| }}> | |
| <div style={{ fontSize: '1.75rem', fontWeight: '700' }}>{dti.toFixed(1)}%</div> | |
| <div style={{ fontSize: '0.75rem', color: '#6b7280', fontWeight: '600' }}>DEBT-TO-INCOME</div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export function ComparisonRadar({ userResults }) { | |
| const benchmarks = JSON.parse(userResults.benchmarks_json || "{}"); | |
| // Clean keys for display | |
| const labelMap = { | |
| 'ApplicantIncome': 'Income', | |
| 'LoanAmount': 'Loan Size', | |
| 'Total_Income': 'Total Rev', | |
| 'EMI': 'EMI Load', | |
| 'Credit_History': 'Credit Score' | |
| }; | |
| const keys = Object.keys(labelMap); | |
| // Normalize values roughly for radar (0-100 scale) | |
| const normalize = (val, max) => Math.min(100, (val / max) * 100); | |
| const userData = [ | |
| normalize(userResults.applicant_income, benchmarks.ApplicantIncome * 2 || 10000), | |
| normalize(userResults.loan_amount, benchmarks.LoanAmount * 2 || 500), | |
| normalize(userResults.applicant_income + userResults.coapplicant_income, benchmarks.Total_Income * 2 || 15000), | |
| normalize((userResults.loan_amount * 1000) / userResults.loan_amount_term, benchmarks.EMI * 2 || 2000), | |
| userResults.credit_history * 100 | |
| ]; | |
| const benchData = [50, 50, 50, 50, 50]; // Benchmarks are normalized to center | |
| const data = { | |
| labels: keys.map(k => labelMap[k]), | |
| datasets: [ | |
| { | |
| label: 'Your Profile', | |
| data: userData, | |
| backgroundColor: 'rgba(242, 98, 47, 0.2)', | |
| borderColor: '#f2622f', | |
| borderWidth: 2, | |
| pointBackgroundColor: '#f2622f', | |
| }, | |
| { | |
| label: 'Approved Average', | |
| data: benchData, | |
| backgroundColor: 'rgba(107, 114, 128, 0.1)', | |
| borderColor: '#9ca3af', | |
| borderWidth: 1, | |
| borderDash: [5, 5], | |
| pointRadius: 0 | |
| } | |
| ] | |
| }; | |
| const options = { | |
| scales: { | |
| r: { | |
| angleLines: { display: false }, | |
| suggestedMin: 0, | |
| suggestedMax: 100, | |
| ticks: { display: false } | |
| } | |
| }, | |
| plugins: { | |
| legend: { position: 'bottom', labels: { boxWidth: 12, font: { size: 10 } } } | |
| } | |
| }; | |
| return <Radar data={data} options={options} />; | |
| } | |
| export function FeatureImportanceBar({ featuresJson }) { | |
| let features = {}; | |
| try { | |
| features = JSON.parse(featuresJson); | |
| } catch(e) { | |
| return null; | |
| } | |
| const sorted = Object.entries(features) | |
| .sort(([, a], [, b]) => b - a) | |
| .slice(0, 5); | |
| const data = { | |
| labels: sorted.map(([k]) => k.replace(/_/g, ' ')), | |
| datasets: [{ | |
| label: 'Impact Score', | |
| data: sorted.map(([,v]) => v), | |
| backgroundColor: '#f2622f', | |
| borderRadius: 6 | |
| }] | |
| }; | |
| const options = { | |
| indexAxis: 'y', | |
| plugins: { legend: { display: false } }, | |
| scales: { | |
| x: { display: false }, | |
| y: { grid: { display: false }, ticks: { font: { size: 11 } } } | |
| } | |
| }; | |
| return <Bar data={data} options={options} />; | |
| } | |