Spotix-API / frontend /components /shared /PageLoader.tsx
Anish-530
Fix: 404 page loader stuck, navbar loading spinners, bulletproof NR import
942a467
"use client";
import React, { useState, useEffect } from 'react';
import { usePathname } from 'next/navigation';
export default function PageLoader({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const [isTransitioning, setIsTransitioning] = useState(false);
const [loadProgress, setLoadProgress] = useState(0);
const [isLoaded, setIsLoaded] = useState(true);
const initialMount = React.useRef(true);
useEffect(() => {
if (initialMount.current && pathname === '/') {
initialMount.current = false;
return;
}
initialMount.current = false;
setIsTransitioning(true);
setIsLoaded(false);
setLoadProgress(0);
let progress = 0;
// Safety net: force complete after 2500ms even if __PAGE_LOADED never fires
// (e.g. the 404 page, or any page that doesn't set the flag)
const safetyTimeout = setTimeout(() => {
(window as any).__PAGE_LOADED = true;
}, 2500);
const interval = setInterval(() => {
if (progress < 90) {
progress += Math.random() * 15 + 5;
} else if ((window as any).__PAGE_LOADED) {
progress = 100;
}
if (progress >= 100) {
progress = 100;
clearInterval(interval);
setIsLoaded(true);
setTimeout(() => setIsTransitioning(false), 800);
}
setLoadProgress(progress);
}, 80);
return () => {
clearInterval(interval);
clearTimeout(safetyTimeout);
// Always reset the flag on cleanup so next page starts fresh
(window as any).__PAGE_LOADED = false;
};
}, [pathname]);
return (
<>
<style dangerouslySetInnerHTML={{
__html: `
#global-loader { position: fixed; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999; overflow: hidden; background: transparent; pointer-events: none; }
.global-loader-panel { position: absolute; left: 0; width: 100%; height: 50%; background: var(--theme-bg); transition: transform 0.8s cubic-bezier(0.85, 0, 0.15, 1); z-index: 10000; will-change: transform; pointer-events: auto; }
.global-loader-panel.top { top: 0; margin-bottom: -1px; }
.global-loader-panel.bottom { bottom: 0; margin-top: -1px; }
.global-loaded .global-loader-panel.top { transform: translateY(-101%); }
.global-loaded .global-loader-panel.bottom { transform: translateY(101%); }
.global-loader-content { position: relative; z-index: 10001; transition: opacity 0.4s ease; pointer-events: auto; }
.global-loaded .global-loader-content { opacity: 0; pointer-events: none; }
#global-progress-container { width: 200px; height: 1px; background: rgba(253, 232, 214, 0.1); margin-top: 20px; position: relative; overflow: hidden; }
#global-progress-fill { position: absolute; top: 0; left: 0; height: 100%; background: var(--theme-text); transition: width 0.1s linear; }
`}} />
{isTransitioning && (
<div id="global-loader" className={isLoaded ? 'global-loaded' : ''}>
<div className="global-loader-panel top"></div>
<div className="global-loader-panel bottom"></div>
<div className="global-loader-content flex flex-col items-center">
<div className="text-[10px] font-mono tracking-[0.5em] text-[var(--theme-text)] uppercase mb-4">Initializing Module</div>
<div id="global-progress-container">
<div id="global-progress-fill" style={{ width: `${Math.min(100, loadProgress)}%` }}></div>
</div>
<div className="text-[10px] font-mono text-[var(--theme-text)] mt-4 opacity-50">{Math.floor(Math.min(100, loadProgress))}%</div>
</div>
</div>
)}
<div className={`transition-all duration-1000 ${isTransitioning && !isLoaded ? 'opacity-0 blur-sm scale-95' : 'opacity-100 blur-0'}`}>
{children}
</div>
</>
);
}