'use client'; import { useState, useEffect } from 'react'; interface PersonalPattern { total_analyzed: number; number_frequency: Record; top_picks: number[]; least_picks: number[]; pattern: { avg_odd_count: number; avg_sum: number; avg_range: number; consecutive_rate: number; zone_avg: Record; }; vs_draw_avg: { odd_diff: number; sum_diff: number; odd_tendency: string; sum_tendency: string; }; } function getBallStyle(n: number) { if (n <= 10) return { bg: 'linear-gradient(145deg,#fde68a,#fbbf24,#d97706)', text: '#78350f' }; if (n <= 20) return { bg: 'linear-gradient(145deg,#93c5fd,#3b82f6,#1d4ed8)', text: '#fff' }; if (n <= 30) return { bg: 'linear-gradient(145deg,#fca5a5,#ef4444,#b91c1c)', text: '#fff' }; if (n <= 40) return { bg: 'linear-gradient(145deg,#d1d5db,#9ca3af,#4b5563)', text: '#fff' }; return { bg: 'linear-gradient(145deg,#86efac,#22c55e,#15803d)', text: '#fff' }; } function SmallBall({ n, size = 30, freq }: { n: number; size?: number; freq?: number }) { const { bg, text } = getBallStyle(n); return (
{n}
{freq !== undefined &&
{freq}회
}
); } function ZoneBar({ label, value, max }: { label: string; value: number; max: number }) { const pct = max > 0 ? (value / max) * 100 : 0; return (
{label}
{value.toFixed(1)}
); } export default function PatternTab() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); useEffect(() => { fetch('/api/lotto/analysis/personal').then(r => r.json()) .then(d => { if (d?.error) { setError(d.error === 'NAS_TIMEOUT' ? 'NAS 서버 응답 시간 초과.' : '패턴 분석을 불러오지 못했습니다.'); return; } setData(d); }) .catch(() => setError('패턴 분석을 불러오지 못했습니다.')) .finally(() => setLoading(false)); }, []); if (loading) return (
); if (error) return
{error}
; if (!data || data.total_analyzed === 0) return (
📊
아직 분석할 데이터가 없습니다
번호 생성 탭에서 번호를 추천받으면 패턴이 쌓입니다
); const zoneMax = Math.max(...Object.values(data.pattern.zone_avg)); const tendencyColor = (tendency: string) => tendency.includes('고') || tendency.includes('홀수') ? '#f87171' : tendency.includes('저') || tendency.includes('짝수') ? '#60a5fa' : '#4ade80'; return (
PERSONAL PATTERN ANALYSIS

내 번호 선택 패턴

총 분석 {data.total_analyzed}
{/* 자주 선택한 번호 */}
⭐ 자주 선택한 번호 TOP 10
{data.top_picks.map(n => ( ))}
{/* 한 번도 안 쓴 번호 */}
💤 거의 안 쓴 번호
{data.least_picks.map(n => )}
이 번호들도 가끔 포함해보세요
{/* 패턴 지표 */}
📐 선택 패턴 지표
{[ { label: '평균 홀수 개수', value: data.pattern.avg_odd_count.toFixed(1) + '개', ref: '역대 평균 3.0개', refColor: 'rgba(255,255,255,.2)' }, { label: '평균 합계', value: data.pattern.avg_sum.toFixed(0), ref: '역대 평균 138', refColor: 'rgba(255,255,255,.2)' }, { label: '평균 범위(최대-최소)', value: data.pattern.avg_range.toFixed(1), ref: '', refColor: '' }, { label: '연속번호 포함률', value: `${(data.pattern.consecutive_rate * 100).toFixed(0)}%`, ref: '', refColor: '' }, ].map(({ label, value, ref }) => (
{label}
{value}
{ref &&
{ref}
}
))}
{/* 구간별 선택 분포 */}
🎯 구간별 선택 분포
{Object.entries(data.pattern.zone_avg).map(([zone, val]) => ( ))}
{/* 역대 당첨과 비교 */}
⚖️ 역대 당첨 평균과 비교
홀수 선택 경향
{data.vs_draw_avg.odd_tendency}
당첨 평균 대비 {data.vs_draw_avg.odd_diff > 0 ? '+' : ''}{data.vs_draw_avg.odd_diff.toFixed(1)}개
합계 선택 경향
{data.vs_draw_avg.sum_tendency}
당첨 평균 대비 {data.vs_draw_avg.sum_diff > 0 ? '+' : ''}{data.vs_draw_avg.sum_diff.toFixed(1)}
); }