feat(tarot): TodayCard.jsx — 원카드 페이지 (T14)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
98
src/pages/tarot/TodayCard.jsx
Normal file
98
src/pages/tarot/TodayCard.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user