first commit

This commit is contained in:
2025-12-13 20:08:01 +01:00
commit cc34dd0fd9
21 changed files with 5645 additions and 0 deletions

100
src/App.jsx Normal file
View File

@@ -0,0 +1,100 @@
import React, { useState, useEffect } from 'react';
import Timeline from './components/Timeline';
import PhotoDetail from './components/PhotoDetail';
import Admin from './components/Admin';
import { fetchPhotos } from './data/photos';
import { Settings, Sun, Moon } from 'lucide-react';
function App() {
const [view, setView] = useState(() => {
return window.location.pathname === '/admin' ? 'admin' : 'timeline';
});
const [activePhoto, setActivePhoto] = useState(null);
const [photos, setPhotos] = useState([]);
// Theme State
const [theme, setTheme] = useState(() => {
return localStorage.getItem('theme_preference') || 'dark';
});
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme_preference', theme);
}, [theme]);
const toggleTheme = () => {
setTheme(prev => prev === 'dark' ? 'light' : 'dark');
};
const loadPhotos = async () => {
const data = await fetchPhotos();
setPhotos(data);
};
useEffect(() => {
loadPhotos();
}, [view]);
// Handle browser back/forward
useEffect(() => {
const handlePopState = () => {
setView(window.location.pathname === '/admin' ? 'admin' : 'timeline');
};
window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, []);
// Update URL when view changes
useEffect(() => {
const path = view === 'admin' ? '/admin' : '/';
if (window.location.pathname !== path) {
window.history.pushState({}, '', path);
}
}, [view]);
if (view === 'admin') {
return <Admin onBack={() => setView('timeline')} onUpdate={loadPhotos} />;
}
return (
<div className="app-container">
{/* Theme Toggle */}
<div style={{ position: 'fixed', top: '20px', right: '20px', zIndex: 50 }}>
<button
onClick={toggleTheme}
style={{
background: 'transparent',
color: 'var(--text-primary)',
padding: '8px',
borderRadius: '50%',
border: 'none',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
opacity: 0.7,
transition: 'opacity 0.2s'
}}
onMouseEnter={(e) => e.currentTarget.style.opacity = '1'}
onMouseLeave={(e) => e.currentTarget.style.opacity = '0.7'}
>
{theme === 'dark' ? <Sun size={20} color="white" /> : <Moon size={20} color="black" />}
</button>
</div>
{/* Main Timeline View */}
<Timeline photos={photos} onSelectPhoto={setActivePhoto} />
{/* Detail Overlay */}
{activePhoto && (
<PhotoDetail
photo={activePhoto}
onClose={() => setActivePhoto(null)}
/>
)}
</div>
);
}
export default App;