From 4c82fa9b2112c1c8fc1bad8a3d94425ad560893c Mon Sep 17 00:00:00 2001 From: gahusb Date: Sun, 24 May 2026 00:39:22 +0900 Subject: [PATCH] =?UTF-8?q?feat(tarot):=20TarotCard=C2=B7CardGrid=C2=B7Spr?= =?UTF-8?q?eadSlots=C2=B7InterpretationPanel=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20(T11)?= 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/components/CardGrid.jsx | 21 +++++ .../tarot/components/InterpretationPanel.jsx | 92 +++++++++++++++++++ src/pages/tarot/components/SpreadSlots.jsx | 27 ++++++ src/pages/tarot/components/TarotCard.jsx | 50 ++++++++++ 4 files changed, 190 insertions(+) create mode 100644 src/pages/tarot/components/CardGrid.jsx create mode 100644 src/pages/tarot/components/InterpretationPanel.jsx create mode 100644 src/pages/tarot/components/SpreadSlots.jsx create mode 100644 src/pages/tarot/components/TarotCard.jsx diff --git a/src/pages/tarot/components/CardGrid.jsx b/src/pages/tarot/components/CardGrid.jsx new file mode 100644 index 0000000..e550008 --- /dev/null +++ b/src/pages/tarot/components/CardGrid.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import TarotCard from './TarotCard'; + +export default function CardGrid({ slice, onPick, disabledIds = [] }) { + return ( +
+ {slice.map((card) => { + const disabled = disabledIds.includes(card.slug); + return ( + !disabled && onPick(card)} + /> + ); + })} +
+ ); +} diff --git a/src/pages/tarot/components/InterpretationPanel.jsx b/src/pages/tarot/components/InterpretationPanel.jsx new file mode 100644 index 0000000..0961cef --- /dev/null +++ b/src/pages/tarot/components/InterpretationPanel.jsx @@ -0,0 +1,92 @@ +import React, { useState } from 'react'; + +function ConfidenceBadge({ level }) { + if (!level) return null; + const cls = level === 'high' ? 'is-high' : level === 'low' ? 'is-low' : 'is-medium'; + const text = level === 'high' ? '높음' : level === 'low' ? '낮음' : '보통'; + return 확신 {text}; +} + +export default function InterpretationPanel({ interpretation, selectedCard, focusCardId }) { + const [showEvidence, setShowEvidence] = useState(true); + + if (!interpretation) { + return ( + + ); + } + + const cardDetail = focusCardId + ? (interpretation.cards || []).find((c) => c.card === focusCardId) + : null; + + return ( + + ); +} diff --git a/src/pages/tarot/components/SpreadSlots.jsx b/src/pages/tarot/components/SpreadSlots.jsx new file mode 100644 index 0000000..48372aa --- /dev/null +++ b/src/pages/tarot/components/SpreadSlots.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import TarotCard from './TarotCard'; + +export default function SpreadSlots({ spread, picks, onCardClick }) { + return ( +
+ {spread.positions.map((pos) => { + const pick = picks[pos.idx]; + return ( +
+
{pos.label}
+ {pick ? ( + onCardClick(pos.idx)} + /> + ) : ( +
_
+ )} +
+ ); + })} +
+ ); +} diff --git a/src/pages/tarot/components/TarotCard.jsx b/src/pages/tarot/components/TarotCard.jsx new file mode 100644 index 0000000..e485b8d --- /dev/null +++ b/src/pages/tarot/components/TarotCard.jsx @@ -0,0 +1,50 @@ +import React from 'react'; + +export default function TarotCard({ + card, reversed = false, size = 'md', faceDown = false, + clickable = false, onClick, label, +}) { + const sizeClass = size === 'sm' ? 'tarot-card--sm' : size === 'lg' ? 'tarot-card--lg' : 'tarot-card--md'; + + const handleClick = (e) => { + if (!clickable) return; + onClick?.(card, e); + }; + + if (faceDown || !card) { + return ( + + ); + } + + const styleClass = reversed ? 'tarot-card--reversed' : ''; + const onImgError = (e) => { e.currentTarget.style.display = 'none'; }; + + return ( + + ); +}