--- ### **3. public/manifest.json** on { "name": "RailTrack Live", "short_name": "RailTrack", "description": "Real-time Train Information - Where Is My Train Clone", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#0066cc", "orientation": "portrait-primary", "icons": [ { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" } ], "categories": ["travel", "transportation"], "screenshots": [ { "src": "icons/screenshot-1.png", "sizes": "540x720", "type": "image/png", "form_factor": "narrow" } ] } --- ### **4. public/sw.js (Service Worker)** const CACHE_NAME = 'railtrack-v1'; const urlsToCache = [ '/', '/index.html', '/app.js', '/styles.css', '/manifest.json', '/icons/icon-192.png', '/icons/icon-512.png' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll(urlsToCache); }) ); }); self.addEventListener('fetch', event => { if (event.request.method !== 'GET') { return; } event.respondWith( caches.match(event.request).then(response => { if (response) { return response; } return fetch(event.request).then(response => { if (!response || response.status !== 200 || response.type !== 'basic') { return response; } const responseToCache = response.clone(); caches.open(CACHE_NAME).then(cache => { cache.put(event.request, responseToCache); }); return response; }); }).catch(() => { return caches.match('/index.html'); }) ); }); self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheName !== CACHE_NAME) { return caches.delete(cacheName); } }) ); }) ); }); --- ### **5. src/App.jsx (Main Component)** function App() { const [currentPage, setCurrentPage] = useState('home'); const [darkMode, setDarkMode] = useState( window.matchMedia('(prefers-color-scheme: dark)').matches ); const [selectedTrain, setSelectedTrain] = useState(null); const [searchResults, setSearchResults] = useState([]); useEffect(() => { if (darkMode) { document.body.classList.add('dark-mode'); } else { document.body.classList.remove('dark-mode'); } localStorage.setItem('darkMode', darkMode); }, [darkMode]); const handleSearch = (results) => { setSearchResults(results); setCurrentPage('search'); }; const handleSelectTrain = (train) => { setSelectedTrain(train); setCurrentPage('details'); }; const renderPage = () => { switch (currentPage) { case 'home': return ; case 'search': return ; case 'details': return ; case 'pnr': return ; case 'settings': return ; default: return ; } }; return (
{renderPage()}
); } App; --- ### **6. src/components/Header.jsx** function Header({ darkMode, setDarkMode }) { return (

RailTrack

); } Header; --- ### **7. src/components/SearchBar.jsx** function SearchBar({ onSearch, placeholder = "Search trains..." }) { const [query, setQuery] = useState(''); const [loading, setLoading] = useState(false); const handleSearch = async (e) => { e.preventDefault(); if (!query.trim()) return; setLoading(true); try { const response = await fetch(`/api/trains/search?q=${query}`); const data = await response.json(); onSearch(data); } catch (error) { console.error('Search error:', error); } setLoading(false); }; return (
e.currentTarget.style.borderColor = 'var(--primary)'} onBlur={(e) => e.currentTarget.style.borderColor = 'var(--border-light)'} > setQuery(e.target.value)} placeholder={placeholder} style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', padding: '0.75rem', fontSize: '1rem', color: 'var(--text-light)' }} />
); } SearchBar; --- ### **8. src/components/TrainCard.jsx** function TrainCard({ train, onSelect }) { const [isFavorited, setIsFavorited] = useState(false); const handleAddToFavorites = (e) => { e.stopPropagation(); setIsFavorited(!isFavorited); const favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); if (!isFavorited) { favorites.push(train); } else { const index = favorites.findIndex(t => t.number === train.number); if (index > -1) favorites.splice(index, 1); } localStorage.setItem('favorites', JSON.stringify(favorites)); }; return (
onSelect(train)} style={{ background: 'var(--card-light)', border: '1px solid var(--border-light)', borderRadius: '12px', padding: '1rem', marginBottom: '1rem', cursor: 'pointer', transition: 'all 0.3s', animation: 'fadeIn 0.4s ease-out' }} onMouseEnter={(e) => { e.currentTarget.style.boxShadow = '0 10px 25px rgba(0,0,0,0.1)'; e.currentTarget.style.transform = 'translateY(-2px)'; }} onMouseLeave={(e) => { e.currentTarget.style.boxShadow = 'none'; e.currentTarget.style.transform = 'translateY(0)'; }} className="fade-in" >

{train.name}

#{train.number}

From

{train.from}

To

{train.to}

{train.status}
{train.delay ? `+${train.delay} min` : 'On schedule'}
); } TrainCard; --- ### **9. src/components/PNRChecker.jsx** function PNRChecker({ onCheck }) { const [pnr, setPnr] = useState(''); const [loading, setLoading] = useState(false); const handleCheck = async (e) => { e.preventDefault(); if (!pnr.trim() || pnr.length !== 10) { alert('Please enter a valid 10-digit PNR'); return; } setLoading(true); try { const response = await fetch(`/api/pnr/status?pnr=${pnr}`); const data = await response.json(); onCheck(data); } catch (error) { console.error('PNR check error:', error); alert('Failed to fetch PNR status'); } setLoading(false); }; return (

Check PNR Status

setPnr(e.target.value.slice(0, 10))} placeholder="Enter 10-digit PNR" maxLength="10" style={{ flex: 1, border: 'none', background: 'rgba(255,255,255,0.2)', padding: '0.75rem 1rem', borderRadius: '8px', color: 'white', fontSize: '1rem', outline: 'none' }} />
); } PNRChecker; --- ### **10. src/components/RouteMap.jsx** function RouteMap({ route }) { return (

Route Map

{route.stations.map((station, index) => (
{index + 1}

{station.name}

{station.time}

{index < route.stations.length - 1 && (
)}
))}
); } RouteMap; --- ### **11. src/components/StationLive.jsx** function StationLive({ stationCode }) { const [arrivals, setArrivals] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { const fetchArrivals = async () => { setLoading(true); try { const response = await fetch(`/api/stations/arrivals?code=${stationCode}`); const data = await response.json(); setArrivals(data); } catch (error) { console.error('Error fetching arrivals:', error); } setLoading(false); }; if (stationCode) { fetchArrivals(); const interval = setInterval(fetchArrivals, 30000); return () => clearInterval( const Component = App; const root = ReactDOM.createRoot(document.getElementById('root')); root.render();