feat(tarot): TodayCard.jsx — 원카드 페이지 (T14)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 00:45:01 +09:00
parent 1387d91ac5
commit 44bbff297f

View File

@@ -0,0 +1,98 @@
import React, { useState } from 'react';
import './Tarot.css';
import { TAROT_DECK, CATEGORIES } from './data/cards';
import { useTarotReading } from './hooks/useTarotReading';
import TarotCard from './components/TarotCard';
import InterpretationPanel from './components/InterpretationPanel';
export default function TodayCard() {
const [category, setCategory] = useState('일반');
const [question, setQuestion] = useState('');
const [pick, setPick] = useState(null);
const { status, interpretation, runInterpretAndSave, error } = useTarotReading();
const drawCard = () => {
const idx = Math.floor(Math.random() * TAROT_DECK.length);
const reversed = Math.random() < 0.5;
const card = TAROT_DECK[idx];
setPick({ card, position: '오늘', reversed });
};
const handleStart = () => {
drawCard();
};
const handleInterpret = async () => {
if (!pick) return;
try {
await runInterpretAndSave({
spread_type: 'one_card',
category,
question: question.trim() || null,
picks: [pick],
});
} catch (e) {
// error는 hook의 state로 전달됨
}
};
const selectedCardMeaning = pick?.card || null;
const focusCardId = pick?.card?.slug;
const busy = status === 'interpreting' || status === 'saving';
return (
<div className="tarot tarot-reading">
<aside className="tarot-reading__col">
<div className="tarot-reading__step-label">오늘의 카드</div>
<label className="tarot-reading__step-label">질문 (선택)</label>
<textarea
className="tarot-reading__textarea"
value={question}
onChange={(e) => setQuestion(e.target.value)}
placeholder="오늘 무엇이 궁금한가요?"
/>
<div className="tarot-reading__step-label" style={{ marginTop: 16 }}>카테고리</div>
<div className="tarot-reading__chips">
{CATEGORIES.map((c) => (
<button
key={c}
className={`tarot-chip ${category === c ? 'is-active' : ''}`}
onClick={() => setCategory(c)}
>{c}</button>
))}
</div>
{!pick && (
<button className="tarot-reading__primary" onClick={handleStart}>
카드 뽑기
</button>
)}
{pick && !interpretation && (
<button className="tarot-reading__primary" onClick={handleInterpret} disabled={busy}>
{busy ? '해석 중…' : 'AI 해석 시작'}
</button>
)}
{pick && interpretation && (
<button className="tarot-reading__primary" onClick={() => { setPick(null); }}>
다시 뽑기
</button>
)}
{error && <p style={{ color: '#f43f5e', marginTop: 12, fontSize: 13 }}>오류: {error}</p>}
</aside>
<div className="tarot-reading__col" style={{ display: 'grid', placeItems: 'center', minHeight: 320 }}>
{pick ? (
<TarotCard card={pick.card} reversed={pick.reversed} size="lg" label={pick.position} />
) : (
<p style={{ color: 'var(--tarot-text-dim)' }}>좌측에서 "카드 뽑기" 눌러보세요.</p>
)}
</div>
<InterpretationPanel
interpretation={interpretation}
selectedCard={selectedCardMeaning}
focusCardId={focusCardId}
/>
</div>
);
}