Functions.jsx 컴포넌트 분할: 1,583→460줄 (3훅+8컴포넌트+유틸)

- lottoUtils.jsx: 공통 유틸·상수 추출 (Ball, NumberRow, 통계 헬퍼 등)
- hooks/useLottoData.js: 핵심 데이터 로드 (최신회차, 통계, 시뮬레이션, 리포트)
- hooks/usePurchases.js: 구매 기록 CRUD
- hooks/useManualRecommend.js: 수동 추천 + 히스토리
- components/: MetricBlock, FrequencyChart, PerformanceBanner,
  ConfidenceRing, CombinedRecommendPanel, ReportPanel,
  PersonalAnalysisPanel, PurchasePanel 분리
- getReport import 누락 버그 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 07:53:25 +09:00
parent 2b463682d5
commit 299ce636ff
13 changed files with 1342 additions and 1256 deletions

View File

@@ -0,0 +1,83 @@
import React from 'react';
import { NumberRow } from '../lottoUtils';
const PersonalAnalysisPanel = ({ data, loading }) => {
const zones = Object.entries(data?.pattern?.zone_avg ?? {});
const maxZone = zones.length ? Math.max(...zones.map(([, v]) => Number(v) || 0), 1) : 1;
return (
<section className="lotto-panel lotto-panel--wide">
<div className="lotto-panel__head">
<div>
<p className="lotto-panel__eyebrow">My Pattern</p>
<h3> 번호 패턴</h3>
{data && data.total_analyzed > 0 && (
<p className="lotto-panel__sub"> {data.total_analyzed} 추천 기반 분석</p>
)}
</div>
</div>
{(loading || !data || data.total_analyzed === 0) ? (
<p className="lotto-empty">
{loading ? '불러오는 중...' : '추천 이력이 없습니다.'}
</p>
) : (
<div className="lotto-analysis">
<div className="lotto-analysis__row">
<div className="lotto-analysis__group">
<p className="lotto-analysis__label">
내가 자주 선택한 번호 <span>TOP 10</span>
</p>
<NumberRow nums={data.top_picks ?? []} />
</div>
<div className="lotto-analysis__group">
<p className="lotto-analysis__label">선택 성향</p>
<div className="lotto-personal-tendency">
{data.vs_draw_avg?.odd_tendency && (
<span className="lotto-personal-tendency__badge">
{data.vs_draw_avg.odd_tendency}
</span>
)}
{data.vs_draw_avg?.sum_tendency && (
<span className="lotto-personal-tendency__badge">
{data.vs_draw_avg.sum_tendency}
</span>
)}
</div>
<div className="lotto-analysis__stats">
<span>홀수 평균 <strong>{data.pattern?.avg_odd_count?.toFixed(1)}</strong></span>
<span>합계 평균 <strong>{data.pattern?.avg_sum?.toFixed(1)}</strong></span>
<span>
연속번호 포함률{' '}
<strong>
{((data.pattern?.consecutive_rate ?? 0) * 100).toFixed(0)}%
</strong>
</span>
</div>
</div>
{zones.length > 0 && (
<div className="lotto-analysis__group">
<p className="lotto-analysis__label">구간별 선택 비율</p>
<div className="lotto-buckets">
{zones.map(([zone, avg]) => (
<div key={zone} className="lotto-bucket">
<span className="lotto-bucket__label">{zone}</span>
<div className="lotto-bucket__bar" aria-hidden>
<span style={{ width: `${((Number(avg) || 0) / maxZone) * 100}%` }} />
</div>
<span className="lotto-bucket__value">{Number(avg).toFixed(1)}</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
)}
</section>
);
};
export default PersonalAnalysisPanel;