feat: add Vite entry point and root App component

This commit is contained in:
2026-04-27 08:36:18 +09:00
parent 3e998b3232
commit ca553aface
2 changed files with 92 additions and 0 deletions

69
src/App.tsx Normal file
View File

@@ -0,0 +1,69 @@
import { css } from '@emotion/react';
import { useEffect } from 'react';
import { BottomTabBar } from './components/BottomTabBar';
import { OfflineRewardModal } from './components/OfflineRewardModal';
import { AchievementToast } from './components/AchievementToast';
import { ElementsScreen } from './components/screens/ElementsScreen';
import { EvolutionScreen } from './components/screens/EvolutionScreen';
import { FusionScreen } from './components/screens/FusionScreen';
import { ShopScreen } from './components/screens/ShopScreen';
import { AchievementsScreen } from './components/screens/AchievementsScreen';
import { SettingsScreen } from './components/screens/SettingsScreen';
import { TutorialOverlay } from './components/tutorial/TutorialOverlay';
import { useIdleTick } from './hooks/useIdleTick';
import { useGameStore } from './store/useGameStore';
import { trackGameEvent } from './platform/analytics';
const rootStyle = css`
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #f7f8fa;
overflow: hidden;
font-family:
'Pretendard',
-apple-system,
BlinkMacSystemFont,
sans-serif;
`;
const contentStyle = css`
flex: 1;
overflow-y: auto;
padding-bottom: 72px;
`;
export function App() {
const { activeTab, setActiveTab, elements, gold, elementLevels } = useGameStore();
useIdleTick();
useEffect(() => {
const ownedCount = Object.values(elements).filter((c) => c > 0).length;
const totalLevel = Object.values(elementLevels).reduce((sum, lv) => sum + lv, 0);
trackGameEvent('app_open', {
platform: 'web',
platform_time: new Date().toISOString(),
owned_element_count: ownedCount,
gold,
total_enhance_level: totalLevel,
});
}, []);
return (
<div css={rootStyle}>
<OfflineRewardModal />
<AchievementToast />
<div css={contentStyle}>
{activeTab === 'elements' && <ElementsScreen />}
{activeTab === 'evolution' && <EvolutionScreen />}
{activeTab === 'fusion' && <FusionScreen />}
{activeTab === 'shop' && <ShopScreen />}
{activeTab === 'achievements' && <AchievementsScreen />}
{activeTab === 'settings' && <SettingsScreen />}
</div>
<BottomTabBar activeTab={activeTab} onTabChange={setActiveTab} />
<TutorialOverlay />
</div>
);
}

23
src/main.tsx Normal file
View File

@@ -0,0 +1,23 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
import { initAnalytics } from './platform/analytics';
import { useGameStore, flushGameState } from './store/useGameStore';
initAnalytics();
// 탭 종료/이탈 시 즉시 저장 (throttledStorage 보호)
window.addEventListener('beforeunload', flushGameState);
window.addEventListener('pagehide', flushGameState);
// 첫 진입 시 오프라인 보상 계산
useGameStore.getState().initOffline();
const rootEl = document.getElementById('root');
if (!rootEl) throw new Error('Root element #root not found in index.html');
createRoot(rootEl).render(
<StrictMode>
<App />
</StrictMode>
);