-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathApp.tsx
More file actions
179 lines (153 loc) · 6.22 KB
/
Copy pathApp.tsx
File metadata and controls
179 lines (153 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import { useState, useEffect } from 'react';
import { PageId } from './types';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import PageHome from './components/PageHome';
import PageLearn from './components/PageLearn';
import PagePlayground from './components/PagePlayground';
import PageProjects from './components/PageProjects';
import PageChallenges from './components/PageChallenges';
import PageAbout from './components/PageAbout';
import PageContact from './components/PageContact';
import Page404 from './components/Page404';
import { CheckCircle, AlertTriangle, Info, X } from 'lucide-react';
interface ToastState {
message: string;
type: 'success' | 'info' | 'warning';
visible: boolean;
}
export default function App() {
const [activePage, setActivePage] = useState<PageId>('home');
const [isDarkMode, setIsDarkMode] = useState(false);
// Centralized Toast system to replace blocked alerts in iframe environments
const [toast, setToast] = useState<ToastState>({ message: '', type: 'info', visible: false });
const showToast = (message: string, type: 'success' | 'info' | 'warning' = 'info') => {
setToast({ message, type, visible: true });
};
useEffect(() => {
if (toast.visible) {
const timer = setTimeout(() => {
setToast(prev => ({ ...prev, visible: false }));
}, 4000);
return () => clearTimeout(timer);
}
}, [toast.visible]);
// Unified communication states for sandbox loading
const [sandboxHtml, setSandboxHtml] = useState<string | undefined>(undefined);
const [sandboxCss, setSandboxCss] = useState<string | undefined>(undefined);
const [sandboxJs, setSandboxJs] = useState<string | undefined>(undefined);
// Selection states for projects and challenges
const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null);
const [selectedChallengeId, setSelectedChallengeId] = useState<string | null>(null);
const handlePageChange = (page: PageId) => {
setActivePage(page);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleSelectProject = (projectId: string | null) => {
setSelectedProjectId(projectId);
handlePageChange('projects');
};
const handleSelectChallenge = (challengeId: string | null) => {
setSelectedChallengeId(challengeId);
handlePageChange('challenges');
};
// Callback to dynamically load custom code directly into Playground Sandbox
const handleLoadSandboxCode = (html: string, css: string, js: string) => {
setSandboxHtml(html);
setSandboxCss(css);
setSandboxJs(js);
};
const handleToggleDarkMode = () => {
setIsDarkMode(!isDarkMode);
};
return (
<div className={`min-h-screen flex flex-col justify-between transition-colors duration-300 ${
isDarkMode
? 'bg-[#0b0f19] text-slate-100 dark-mode-active'
: 'bg-neutral-bg text-neutral-text'
}`}>
{/* Dynamic Dark Mode Notice Banner */}
{isDarkMode && (
<div className="bg-primary/10 border-b border-primary/20 py-2 text-center text-[11px] font-semibold text-primary select-none animate-pulse">
⚡ Simulating Midnight Slate Theme Mode. Some visual variables adapted for comfort.
</div>
)}
{/* STICKY NAVBAR */}
<Navbar
activePage={activePage}
onPageChange={handlePageChange}
isDarkMode={isDarkMode}
onToggleDarkMode={handleToggleDarkMode}
onShowToast={showToast}
/>
{/* MAIN VIEWPORT */}
<main className="flex-grow w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
{activePage === 'home' && (
<PageHome
onPageChange={handlePageChange}
onSelectProject={handleSelectProject}
onSelectChallenge={handleSelectChallenge}
onShowToast={showToast}
/>
)}
{activePage === 'learn' && (
<PageLearn
onPageChange={handlePageChange}
onLoadSandboxCode={handleLoadSandboxCode}
onShowToast={showToast}
/>
)}
{activePage === 'playground' && (
<PagePlayground
initialHtml={sandboxHtml}
initialCss={sandboxCss}
initialJs={sandboxJs}
/>
)}
{activePage === 'projects' && (
<PageProjects
onPageChange={handlePageChange}
selectedProjectId={selectedProjectId}
onSelectProject={setSelectedProjectId}
onLoadSandboxCode={handleLoadSandboxCode}
/>
)}
{activePage === 'challenges' && (
<PageChallenges
onPageChange={handlePageChange}
selectedChallengeId={selectedChallengeId}
onSelectChallenge={setSelectedChallengeId}
/>
)}
{activePage === 'about' && (
<PageAbout onPageChange={handlePageChange} />
)}
{activePage === 'contact' && (
<PageContact />
)}
</main>
{/* PROFESSIONAL FOOTER */}
<Footer onPageChange={handlePageChange} />
{/* Central Notification Toast System */}
{toast.visible && (
<div className={`fixed bottom-6 right-6 z-[9999] flex items-center gap-3 px-4.5 py-3.5 rounded-2xl shadow-xl border text-xs sm:text-sm font-semibold transition-all duration-300 animate-bounce ${
toast.type === 'success' ? 'bg-emerald-50 border-emerald-200 text-emerald-800' :
toast.type === 'warning' ? 'bg-amber-50 border-amber-200 text-amber-800' :
'bg-blue-50 border-blue-200 text-blue-800'
}`} id="app-toast-alert">
{toast.type === 'success' && <CheckCircle className="h-5 w-5 text-emerald-500 shrink-0" />}
{toast.type === 'warning' && <AlertTriangle className="h-5 w-5 text-amber-500 shrink-0" />}
{toast.type === 'info' && <Info className="h-5 w-5 text-primary shrink-0" />}
<span className="leading-snug">{toast.message}</span>
<button onClick={() => setToast(prev => ({ ...prev, visible: false }))} className="ml-2 hover:opacity-70 p-1 cursor-pointer">
<X className="h-4 w-4 text-slate-400 hover:text-slate-600" />
</button>
</div>
)}
</div>
);
}