feat: 튜토리얼 data-tutorial 마커 및 TutorialOverlay 앱 통합 (JSA-47)
- ElementsScreen: 첫 번째 obtained 원소 카드에 data-tutorial 스포트라이트 마커 - FusionScreen: 합성 슬롯1, 합성 결과 배너에 data-tutorial 마커 - EvolutionScreen: 첫 번째 강화 버튼에 data-tutorial 마커 - _app.tsx: AppContainer에 <TutorialOverlay /> 전역 마운트 Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -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<InitialProps>) {
|
||||
return (
|
||||
<TDSMobileAITProvider brandPrimaryColor="#FF6B35">
|
||||
{children}
|
||||
<TutorialOverlay />
|
||||
</TDSMobileAITProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div css={obtainedCardStyle} onClick={() => onSelect(el)}>
|
||||
<div
|
||||
css={obtainedCardStyle}
|
||||
onClick={() => onSelect(el)}
|
||||
data-tutorial={isTutorialTarget ? 'elements-first-card' : undefined}
|
||||
>
|
||||
<div css={spriteWrapStyle}>
|
||||
<CharacterSprite elementId={el.id} elementColor={el.color} tier={el.tier} size={56} state="obtained" />
|
||||
</div>
|
||||
@@ -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}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -220,7 +220,7 @@ export function EvolutionScreen() {
|
||||
<div css={goldBadgeStyle}>💰 {gold}</div>
|
||||
</div>
|
||||
|
||||
{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
|
||||
? '✅ 최대 레벨 달성'
|
||||
|
||||
@@ -502,6 +502,7 @@ export function FusionScreen() {
|
||||
<div
|
||||
css={slotStyle(!!slot1, selectingSlot === 1)}
|
||||
onClick={() => handleSlotClick(1)}
|
||||
data-tutorial="fusion-slot1"
|
||||
>
|
||||
{slot1 ? (
|
||||
<>
|
||||
@@ -546,7 +547,10 @@ export function FusionScreen() {
|
||||
|
||||
{/* 합성 결과 피드백 */}
|
||||
{lastResult?.type === 'success' && lastResult.resultId && (
|
||||
<div css={resultBannerStyle(elementMap[lastResult.resultId]?.rarity ?? 'common')}>
|
||||
<div
|
||||
css={resultBannerStyle(elementMap[lastResult.resultId]?.rarity ?? 'common')}
|
||||
data-tutorial="fusion-result"
|
||||
>
|
||||
<span css={resultEmojiStyle(elementMap[lastResult.resultId]?.rarity ?? 'common')}>
|
||||
{elementMap[lastResult.resultId]?.emoji}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user