refactor(evolver): Lotto 탭으로 통합 + 다크 테마 + activity 스크롤
- EvolverTab.jsx 신규 생성: evolver 컴포넌트를 탭 body로 추출 - Evolver.jsx → Lotto 페이지 thin wrapper로 교체 (/lotto/evolver URL 유지) - Lotto.jsx: useLocation으로 pathname 감지 → initialTab 결정 - Functions.jsx: 4번째 탭 '🧬 자율 학습' 추가 + initialTab prop 수용 - Evolver.css: light → dark 테마 전환 (rgba/slate 팔레트), activity-list max-height+scroll 적용 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,82 +1,7 @@
|
||||
import React from 'react';
|
||||
import './Evolver.css';
|
||||
import { useEvolverApi } from './evolver/useEvolverApi';
|
||||
import WinnerCard from './evolver/WinnerCard';
|
||||
import TrialsGrid from './evolver/TrialsGrid';
|
||||
import BaseDiff from './evolver/BaseDiff';
|
||||
import BaseHistory from './evolver/BaseHistory';
|
||||
import LottoActivityTimeline from './evolver/LottoActivityTimeline';
|
||||
import EvolverActions from './evolver/EvolverActions';
|
||||
import Lotto from './Lotto';
|
||||
|
||||
// /lotto/evolver URL → Lotto 페이지가 useLocation으로 활성 탭 자동 선택
|
||||
export default function Evolver() {
|
||||
const { status, history, activity, loading, error, refetch } = useEvolverApi({ days: 7, weeks: 12 });
|
||||
|
||||
if (loading) return <div className="evolver"><p>로딩 중...</p></div>;
|
||||
if (error) return <div className="evolver"><p>에러: {String(error)}</p></div>;
|
||||
|
||||
const latestBase = (history.items || [])[0];
|
||||
const previousBase = (history.items || [])[1]?.weight || status?.current_base || [0.2, 0.2, 0.2, 0.2, 0.2];
|
||||
const newBase = latestBase?.weight || status?.current_base;
|
||||
|
||||
const trials = status?.trials || [];
|
||||
const winnerTrialId = latestBase?.source_trial_id;
|
||||
const winnerTrial = trials.find(t => t.id === winnerTrialId);
|
||||
const winnerInfo = winnerTrial ? {
|
||||
day_of_week: winnerTrial.day_of_week,
|
||||
weight: winnerTrial.weight,
|
||||
avg_score: latestBase?.winner_score,
|
||||
max_correct: latestBase?.winner_max_correct,
|
||||
n_picks: (winnerTrial.picks || []).length,
|
||||
} : null;
|
||||
|
||||
const perDay = trials.map(t => ({
|
||||
day_of_week: t.day_of_week,
|
||||
trial_id: t.id,
|
||||
avg_score: (t.picks || []).reduce((s, p) => s + (p.meta_score || 0), 0) / Math.max(1, (t.picks || []).length),
|
||||
max_correct: Math.max(0, ...(t.picks || []).map(p => p.correct || 0)),
|
||||
}));
|
||||
|
||||
const hasBase = (history.items || []).length > 0;
|
||||
|
||||
return (
|
||||
<div className="evolver">
|
||||
<header className="evolver-header">
|
||||
<div>
|
||||
<p className="evolver-kicker">Lotto · Weight Evolver</p>
|
||||
<h1>자율 학습 루프</h1>
|
||||
<p className="evolver-sub">
|
||||
매주 6가지 가중치를 시도해서 best 조합을 다음주 base로 학습합니다.
|
||||
{status?.latest_draw && ` 마지막 회차: ${status.latest_draw}회.`}
|
||||
</p>
|
||||
</div>
|
||||
<button className="refresh-btn" onClick={refetch}>↻ 새로고침</button>
|
||||
</header>
|
||||
|
||||
{!hasBase ? (
|
||||
<div className="evolver-card empty-state">
|
||||
<h2>아직 학습 시작 전</h2>
|
||||
<p>다음 월요일 09:00에 자동 시작 또는 수동 트리거 사용.</p>
|
||||
<EvolverActions onChange={refetch} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<WinnerCard
|
||||
winner={winnerInfo}
|
||||
previousBase={previousBase}
|
||||
updateReason={latestBase?.update_reason}
|
||||
drawNo={status?.latest_draw}
|
||||
/>
|
||||
<TrialsGrid trials={trials} perDay={perDay} winnerTrialId={winnerTrialId} />
|
||||
<BaseDiff
|
||||
previousBase={previousBase}
|
||||
newBase={newBase}
|
||||
updateReason={latestBase?.update_reason}
|
||||
/>
|
||||
<BaseHistory history={history.items || []} />
|
||||
<LottoActivityTimeline activity={activity} days={7} />
|
||||
<EvolverActions onChange={refetch} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return <Lotto />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user