diff --git a/src/_app.tsx b/src/_app.tsx index 1437b51..c3a3d55 100644 --- a/src/_app.tsx +++ b/src/_app.tsx @@ -4,6 +4,7 @@ import { PropsWithChildren } from 'react'; import { InitialProps } from '@granite-js/react-native'; import { context } from '../require.context'; import { initAnalytics } from './analytics'; +import { TutorialOverlay } from './components/tutorial/TutorialOverlay'; initAnalytics(process.env.NODE_ENV !== 'production'); @@ -11,6 +12,7 @@ function AppContainer({ children }: PropsWithChildren) { return ( {children} + ); } diff --git a/src/components/screens/ElementsScreen.tsx b/src/components/screens/ElementsScreen.tsx index 25a0219..d1b067b 100644 --- a/src/components/screens/ElementsScreen.tsx +++ b/src/components/screens/ElementsScreen.tsx @@ -469,9 +469,10 @@ interface ElementCardProps { count: number; level: number; onSelect: (el: ElementData) => void; + isTutorialTarget?: boolean; } -const ElementCard = memo(function ElementCard({ el, state, count, level, onSelect }: ElementCardProps) { +const ElementCard = memo(function ElementCard({ el, state, count, level, onSelect, isTutorialTarget }: ElementCardProps) { const spawnRate = calcSpawnRate(el.id, level); if (state === 'undiscovered') { @@ -498,7 +499,11 @@ const ElementCard = memo(function ElementCard({ el, state, count, level, onSelec } return ( -
onSelect(el)}> +
onSelect(el)} + data-tutorial={isTutorialTarget ? 'elements-first-card' : undefined} + >
@@ -593,6 +598,9 @@ export function ElementsScreen() { items: elementsData.filter((el) => el.tier === tier), })); + // 첫 번째 obtained 원소 ID (튜토리얼 스포트라이트용) + const firstObtainedId = obtainedElements[0]?.id ?? null; + const unlockRatio = obtainedElements.length / totalElements; return ( @@ -644,6 +652,7 @@ export function ElementsScreen() { count={elements[el.id] ?? 0} level={elementLevels[el.id] ?? 0} onSelect={setSelectedEl} + isTutorialTarget={el.id === firstObtainedId} /> ))}
diff --git a/src/components/screens/EvolutionScreen.tsx b/src/components/screens/EvolutionScreen.tsx index 7e5ccc3..f3e37de 100644 --- a/src/components/screens/EvolutionScreen.tsx +++ b/src/components/screens/EvolutionScreen.tsx @@ -220,7 +220,7 @@ export function EvolutionScreen() {
💰 {gold}
- {ownedElements.map((el) => { + {ownedElements.map((el, index) => { const level = elementLevels[el.id] ?? 0; const isMax = level >= ENHANCE_MAX_LEVEL; const cost = isMax ? 0 : calcEnhanceCost(level); @@ -257,6 +257,7 @@ export function EvolutionScreen() { css={enhanceButtonStyle(canEnhance)} onClick={(e) => handleEnhance(el.id, e.currentTarget)} disabled={!canEnhance} + data-tutorial={index === 0 ? 'elements-enhance-button' : undefined} > {isMax ? '✅ 최대 레벨 달성' diff --git a/src/components/screens/FusionScreen.tsx b/src/components/screens/FusionScreen.tsx index 0d311f5..fb3edf9 100644 --- a/src/components/screens/FusionScreen.tsx +++ b/src/components/screens/FusionScreen.tsx @@ -502,6 +502,7 @@ export function FusionScreen() {
handleSlotClick(1)} + data-tutorial="fusion-slot1" > {slot1 ? ( <> @@ -546,7 +547,10 @@ export function FusionScreen() { {/* 합성 결과 피드백 */} {lastResult?.type === 'success' && lastResult.resultId && ( -
+
{elementMap[lastResult.resultId]?.emoji}