fix(tarot): 해석 완료 시 자동 AI 탭 전환 + 새 리딩 시 해석 state 잔존 버그
Issue 1 — 우측 패널 탭이 hardcoded is-active로 클릭/state 없음: - InterpretationPanel에 activeTab state 추가 - useEffect로 interpretation 도착 시 자동 'ai' 탭 전환, 없으면 'card' - 두 탭 콘텐츠 분리: card=카드 의미·상징·조언, ai=위치 해석·종합·상호작용·근거 Issue 2 — useTarotReading hook의 interpretation이 새 리딩 시작에 잔존: - hook에 reset() 함수 추가 (status/interpretation/readingId/error 초기화) - Reading.jsx의 startShuffle/openCardSpread/restart/resetCards/changeSpread 5개 액션에서 모두 reset() 호출 — 새 리딩 시작 시 이전 해석 완전 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,19 +53,21 @@ export default function Reading() {
|
|||||||
|
|
||||||
const spread = SPREADS[spreadId];
|
const spread = SPREADS[spreadId];
|
||||||
const { slice, reshuffle } = useTarotShuffle(TAROT_DECK, 20);
|
const { slice, reshuffle } = useTarotShuffle(TAROT_DECK, 20);
|
||||||
const { status, interpretation, runInterpretAndSave, error } = useTarotReading();
|
const { status, interpretation, runInterpretAndSave, error, reset } = useTarotReading();
|
||||||
|
|
||||||
const startShuffle = () => {
|
const startShuffle = () => {
|
||||||
reshuffle();
|
reshuffle();
|
||||||
setPicks([]);
|
setPicks([]);
|
||||||
setFocusIdx(null);
|
setFocusIdx(null);
|
||||||
setStep(1);
|
setStep(1);
|
||||||
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const openCardSpread = () => {
|
const openCardSpread = () => {
|
||||||
setPicks([]);
|
setPicks([]);
|
||||||
setFocusIdx(null);
|
setFocusIdx(null);
|
||||||
setStep(2);
|
setStep(2);
|
||||||
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePick = (card = null) => {
|
const handlePick = (card = null) => {
|
||||||
@@ -92,6 +94,7 @@ export default function Reading() {
|
|||||||
|
|
||||||
const restart = () => {
|
const restart = () => {
|
||||||
setStep(1); setPicks([]); setFocusIdx(null);
|
setStep(1); setPicks([]); setFocusIdx(null);
|
||||||
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetCards = () => {
|
const resetCards = () => {
|
||||||
@@ -99,6 +102,7 @@ export default function Reading() {
|
|||||||
setPicks([]);
|
setPicks([]);
|
||||||
setFocusIdx(null);
|
setFocusIdx(null);
|
||||||
setStep(1);
|
setStep(1);
|
||||||
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeSpread = (nextSpreadId) => {
|
const changeSpread = (nextSpreadId) => {
|
||||||
@@ -107,6 +111,7 @@ export default function Reading() {
|
|||||||
setFocusIdx(null);
|
setFocusIdx(null);
|
||||||
setStep(1);
|
setStep(1);
|
||||||
reshuffle();
|
reshuffle();
|
||||||
|
reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
const disabledIds = picks.map((p) => p.card.slug);
|
const disabledIds = picks.map((p) => p.card.slug);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
function ConfidenceBadge({ level }) {
|
function ConfidenceBadge({ level }) {
|
||||||
if (!level) return null;
|
if (!level) return null;
|
||||||
@@ -14,15 +14,36 @@ export default function InterpretationPanel({
|
|||||||
selectedPosition,
|
selectedPosition,
|
||||||
selectedReversed = false,
|
selectedReversed = false,
|
||||||
}) {
|
}) {
|
||||||
|
const [activeTab, setActiveTab] = useState('card');
|
||||||
const [showEvidence, setShowEvidence] = useState(true);
|
const [showEvidence, setShowEvidence] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setActiveTab(interpretation ? 'ai' : 'card');
|
||||||
|
}, [interpretation]);
|
||||||
|
|
||||||
|
const hasAI = !!interpretation;
|
||||||
|
|
||||||
|
const renderTabs = () => (
|
||||||
|
<div className="tarot-panel__tabs">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={activeTab === 'card' ? 'is-active' : ''}
|
||||||
|
onClick={() => setActiveTab('card')}
|
||||||
|
>카드 해석</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={activeTab === 'ai' ? 'is-active' : ''}
|
||||||
|
onClick={() => setActiveTab('ai')}
|
||||||
|
disabled={!hasAI}
|
||||||
|
aria-disabled={!hasAI}
|
||||||
|
>AI 인사이트</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
if (!interpretation && !selectedCard) {
|
if (!interpretation && !selectedCard) {
|
||||||
return (
|
return (
|
||||||
<aside className="tarot-panel tarot-panel--reading">
|
<aside className="tarot-panel tarot-panel--reading">
|
||||||
<div className="tarot-panel__tabs">
|
{renderTabs()}
|
||||||
<button type="button" className="is-active">카드 해석</button>
|
|
||||||
<button type="button">AI 인사이트</button>
|
|
||||||
</div>
|
|
||||||
<div className="tarot-panel__empty-state">
|
<div className="tarot-panel__empty-state">
|
||||||
<span>✦</span>
|
<span>✦</span>
|
||||||
<p>카드를 뽑으면 이곳에 카드 의미와 AI 인사이트가 표시됩니다.</p>
|
<p>카드를 뽑으면 이곳에 카드 의미와 AI 인사이트가 표시됩니다.</p>
|
||||||
@@ -44,10 +65,7 @@ export default function InterpretationPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="tarot-panel tarot-panel--reading">
|
<aside className="tarot-panel tarot-panel--reading">
|
||||||
<div className="tarot-panel__tabs">
|
{renderTabs()}
|
||||||
<button type="button" className="is-active">카드 해석</button>
|
|
||||||
<button type="button">AI 인사이트</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{selectedCard && (
|
{selectedCard && (
|
||||||
<header className="tarot-panel__head">
|
<header className="tarot-panel__head">
|
||||||
@@ -65,7 +83,7 @@ export default function InterpretationPanel({
|
|||||||
</header>
|
</header>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedCard && !interpretation && (
|
{activeTab === 'card' && selectedCard && (
|
||||||
<>
|
<>
|
||||||
<section className="tarot-panel__section">
|
<section className="tarot-panel__section">
|
||||||
<h4>카드 의미 요약</h4>
|
<h4>카드 의미 요약</h4>
|
||||||
@@ -86,10 +104,12 @@ export default function InterpretationPanel({
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
{!hasAI && (
|
||||||
<section className="tarot-panel__section tarot-panel__ai-note">
|
<section className="tarot-panel__section tarot-panel__ai-note">
|
||||||
<h4>AI 타로 해석 ✦</h4>
|
<h4>AI 타로 해석 ✦</h4>
|
||||||
<p>모든 카드를 뽑은 뒤 해석 버튼을 누르면 질문과 스프레드 위치를 함께 반영한 리딩이 생성됩니다.</p>
|
<p>모든 카드를 뽑은 뒤 해석 버튼을 누르면 질문과 스프레드 위치를 함께 반영한 리딩이 생성됩니다.</p>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
<div className="tarot-panel__advice-card">
|
<div className="tarot-panel__advice-card">
|
||||||
<span aria-hidden>☾</span>
|
<span aria-hidden>☾</span>
|
||||||
<p>
|
<p>
|
||||||
@@ -100,6 +120,8 @@ export default function InterpretationPanel({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeTab === 'ai' && hasAI && (
|
||||||
|
<>
|
||||||
{cardDetail && (
|
{cardDetail && (
|
||||||
<section className="tarot-panel__section">
|
<section className="tarot-panel__section">
|
||||||
<h4>이 위치의 해석</h4>
|
<h4>이 위치의 해석</h4>
|
||||||
@@ -125,7 +147,6 @@ export default function InterpretationPanel({
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{interpretation && (
|
|
||||||
<section className="tarot-panel__section">
|
<section className="tarot-panel__section">
|
||||||
<h4>종합 해석 <ConfidenceBadge level={interpretation.confidence} /></h4>
|
<h4>종합 해석 <ConfidenceBadge level={interpretation.confidence} /></h4>
|
||||||
<p>{interpretation.summary}</p>
|
<p>{interpretation.summary}</p>
|
||||||
@@ -134,9 +155,8 @@ export default function InterpretationPanel({
|
|||||||
<p className="tarot-panel__warning">⚠️ {interpretation.warning}</p>
|
<p className="tarot-panel__warning">⚠️ {interpretation.warning}</p>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
)}
|
|
||||||
|
|
||||||
{(interpretation?.interactions || []).length > 0 && (
|
{(interpretation.interactions || []).length > 0 && (
|
||||||
<section className="tarot-panel__section">
|
<section className="tarot-panel__section">
|
||||||
<h4>카드 상호작용</h4>
|
<h4>카드 상호작용</h4>
|
||||||
<ul className="tarot-interactions">
|
<ul className="tarot-interactions">
|
||||||
@@ -153,6 +173,8 @@ export default function InterpretationPanel({
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,5 +96,12 @@ export function useTarotReading() {
|
|||||||
return interp.interpretation_json;
|
return interp.interpretation_json;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { status, interpretation, readingId, error, runInterpretAndSave };
|
const reset = useCallback(() => {
|
||||||
|
setStatus('idle');
|
||||||
|
setInterpretation(null);
|
||||||
|
setReadingId(null);
|
||||||
|
setError(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { status, interpretation, readingId, error, runInterpretAndSave, reset };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user