194 lines
6.8 KiB
TypeScript
194 lines
6.8 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Navbar, NavTab, AgeGroup } from './components/Navbar';
|
|
import { StoryReader } from './components/StoryReader';
|
|
import { StoryboardView } from './components/StoryboardView';
|
|
import { ArtDirectionView } from './components/ArtDirectionView';
|
|
import { CharacterVault } from './components/CharacterVault';
|
|
import { AdaptabilityView } from './components/AdaptabilityView';
|
|
import { FormatStrategyView } from './components/FormatStrategyView';
|
|
import { ActivityCorner } from './components/ActivityCorner';
|
|
import { PitchDeckModal } from './components/PitchDeckModal';
|
|
import { BOOK_METADATA } from './data/storyData';
|
|
import { soundManager } from './utils/audio';
|
|
import { Heart } from 'lucide-react';
|
|
|
|
export function App() {
|
|
const [activeTab, setActiveTab] = useState<NavTab>('reader');
|
|
const [ageGroup, setAgeGroup] = useState<AgeGroup>('pictureBook');
|
|
const [isBedtimeMode, setIsBedtimeMode] = useState<boolean>(false);
|
|
const [soundEnabled, setSoundEnabled] = useState<boolean>(true);
|
|
const [isPitchDeckOpen, setIsPitchDeckOpen] = useState<boolean>(false);
|
|
const [storyboardSceneId, setStoryboardSceneId] = useState<number>(1);
|
|
|
|
// Sync global .dark class on <html> with Bedtime Mode so all dark: variants apply
|
|
useEffect(() => {
|
|
const root = document.documentElement;
|
|
if (isBedtimeMode) {
|
|
root.classList.add('dark');
|
|
} else {
|
|
root.classList.remove('dark');
|
|
}
|
|
}, [isBedtimeMode]);
|
|
|
|
const handleExploreStoryboard = (sceneId: number) => {
|
|
soundManager.playPop();
|
|
setStoryboardSceneId(sceneId);
|
|
setActiveTab('storyboard');
|
|
};
|
|
|
|
return (
|
|
<div className={`min-h-screen flex flex-col font-reading transition-colors duration-500 ${
|
|
isBedtimeMode
|
|
? 'bg-slate-950 text-indigo-50'
|
|
: 'bg-[#faf6ee] text-stone-900'
|
|
}`}>
|
|
{/* Top Navigation */}
|
|
<Navbar
|
|
activeTab={activeTab}
|
|
setActiveTab={setActiveTab}
|
|
ageGroup={ageGroup}
|
|
setAgeGroup={setAgeGroup}
|
|
isBedtimeMode={isBedtimeMode}
|
|
setIsBedtimeMode={setIsBedtimeMode}
|
|
soundEnabled={soundEnabled}
|
|
setSoundEnabled={setSoundEnabled}
|
|
onOpenPitchDeck={() => setIsPitchDeckOpen(true)}
|
|
/>
|
|
|
|
{/* Main View Router */}
|
|
<main className="flex-1">
|
|
{activeTab === 'reader' && (
|
|
<StoryReader
|
|
ageGroup={ageGroup}
|
|
setAgeGroup={setAgeGroup}
|
|
isBedtimeMode={isBedtimeMode}
|
|
soundEnabled={soundEnabled}
|
|
onExploreStoryboard={handleExploreStoryboard}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'storyboard' && (
|
|
<StoryboardView
|
|
isBedtimeMode={isBedtimeMode}
|
|
selectedSceneId={storyboardSceneId}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'art' && (
|
|
<ArtDirectionView
|
|
isBedtimeMode={isBedtimeMode}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'characters' && (
|
|
<CharacterVault
|
|
isBedtimeMode={isBedtimeMode}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'adaptability' && (
|
|
<AdaptabilityView
|
|
isBedtimeMode={isBedtimeMode}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'publishing' && (
|
|
<FormatStrategyView
|
|
isBedtimeMode={isBedtimeMode}
|
|
onOpenPitchDeck={() => setIsPitchDeckOpen(true)}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === 'activities' && (
|
|
<ActivityCorner
|
|
isBedtimeMode={isBedtimeMode}
|
|
/>
|
|
)}
|
|
</main>
|
|
|
|
{/* Footer */}
|
|
<footer className={`border-t py-8 px-4 sm:px-6 lg:px-8 transition-colors ${
|
|
isBedtimeMode
|
|
? 'bg-indigo-950/70 border-indigo-900 text-indigo-200'
|
|
: 'bg-[#f7f2e7] border-amber-200/80 text-stone-700'
|
|
}`}>
|
|
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-4 text-xs">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-xl">🦔</span>
|
|
<div>
|
|
<p className="font-heading font-bold text-sm text-stone-900 dark:text-white">
|
|
{BOOK_METADATA.title}
|
|
</p>
|
|
<p className="text-[11px] font-medium text-stone-600 dark:text-indigo-300">
|
|
Children's Storybook Concept & Interactive Story Experience
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center justify-center gap-4 text-xs font-semibold">
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('reader'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Story Reader
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('storyboard'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Storyboard
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('art'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Art Direction
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('characters'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Character Vault
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('adaptability'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Age Matrix
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setActiveTab('publishing'); }}
|
|
className="hover:text-amber-600 transition-colors"
|
|
>
|
|
Publishing Specs
|
|
</button>
|
|
<button
|
|
onClick={() => { soundManager.playPop(); setIsPitchDeckOpen(true); }}
|
|
className="text-amber-700 dark:text-amber-400 font-bold hover:underline"
|
|
>
|
|
Editorial Pitch Deck
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1.5 text-[11px] font-semibold text-stone-700 dark:text-indigo-200">
|
|
<span>Crafted with</span>
|
|
<Heart className="w-3.5 h-3.5 text-rose-500 fill-rose-500" />
|
|
<span>by</span>
|
|
<span className="font-heading font-bold text-stone-900 dark:text-white tracking-wide">aimirah</span>
|
|
<span className="text-stone-500 dark:text-indigo-400">|</span>
|
|
<span className="font-heading font-bold text-amber-800 dark:text-amber-300 tracking-wide">mi4inc</span>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
{/* Editorial Pitch Deck Modal */}
|
|
<PitchDeckModal
|
|
isOpen={isPitchDeckOpen}
|
|
onClose={() => setIsPitchDeckOpen(false)}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|