From d08b20a4b56e95fcfe6753a3efd15c184c54cbf8 Mon Sep 17 00:00:00 2001 From: gahusb Date: Sun, 24 May 2026 00:46:29 +0900 Subject: [PATCH] =?UTF-8?q?feat(tarot):=20Reading.jsx=20=E2=80=94=203?= =?UTF-8?q?=EC=9E=A5=20=EC=8A=A4=ED=94=84=EB=A0=88=EB=93=9C=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20(T15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- src/pages/tarot/Reading.jsx | 142 ++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 src/pages/tarot/Reading.jsx diff --git a/src/pages/tarot/Reading.jsx b/src/pages/tarot/Reading.jsx new file mode 100644 index 0000000..90935b7 --- /dev/null +++ b/src/pages/tarot/Reading.jsx @@ -0,0 +1,142 @@ +import React, { useState } from 'react'; +import './Tarot.css'; +import { TAROT_DECK, CATEGORIES, SPREADS } from './data/cards'; +import { useTarotShuffle } from './hooks/useTarotShuffle'; +import { useTarotReading } from './hooks/useTarotReading'; +import CardGrid from './components/CardGrid'; +import SpreadSlots from './components/SpreadSlots'; +import InterpretationPanel from './components/InterpretationPanel'; + +export default function Reading() { + const [category, setCategory] = useState('일반'); + const [question, setQuestion] = useState(''); + const [spreadId, setSpreadId] = useState('three_card'); + const [step, setStep] = useState(1); // 1: 입력, 2: 뽑기, 3: 해석 + const [picks, setPicks] = useState([]); + const [focusIdx, setFocusIdx] = useState(null); + + const spread = SPREADS[spreadId]; + const { slice, reshuffle } = useTarotShuffle(TAROT_DECK, 16); + const { status, interpretation, runInterpretAndSave, error } = useTarotReading(); + + const startShuffle = () => { + reshuffle(); + setPicks([]); + setFocusIdx(null); + setStep(2); + }; + + const handlePick = (card) => { + if (picks.length >= spread.positions.length) return; + const idx = picks.length; + const pos = spread.positions[idx]; + const next = [...picks, { card, position: pos.label, reversed: card.reversed }]; + setPicks(next); + if (next.length === spread.positions.length) { + setFocusIdx(0); + } + }; + + const handleInterpret = async () => { + try { + await runInterpretAndSave({ + spread_type: spreadId, category, + question: question.trim() || null, picks, + }); + setStep(3); + } catch { /* error는 hook state */ } + }; + + const disabledIds = picks.map((p) => p.card.slug); + const focusCard = focusIdx !== null && picks[focusIdx] ? picks[focusIdx].card : null; + const focusCardId = focusCard?.slug; + const allPicked = picks.length === spread.positions.length; + const busy = status === 'interpreting' || status === 'saving'; + + return ( +
+