// ui.jsx — shared atoms + Header + Footer
(function () {
const { useState, useEffect, useRef } = React;
const Icon = window.Icon;
const D = window.DATA;
/* ---------- Logo ---------- */
function Logo({ light, size = 40, onClick }) {
const logoUrl = (window.INFOTECH && window.INFOTECH.logo) || "";
const mark = logoUrl ? (
) : (
);
return (
{mark}
INFOTECH
Institute of Computer Education
);
}
/* ---------- Placeholder image ---------- */
function Placeholder({ label, icon = "image", style, className, ratio }) {
const r = ratio ? { aspectRatio: ratio } : null;
return (
);
}
/* ---------- Stars ---------- */
function Stars({ n = 5, size = 16 }) {
return (
{[1, 2, 3, 4, 5].map((i) => (
))}
);
}
/* ---------- Scroll reveal ---------- */
function useReveal() {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in-view"); io.unobserve(e.target); } });
}, { threshold: 0.12 });
io.observe(el);
return () => io.disconnect();
}, []);
return ref;
}
/* ---------- Count-up ---------- */
function CountUp({ value }) {
const ref = useRef(null);
const [shown, setShown] = useState(false);
useEffect(() => {
const el = ref.current; if (!el) return;
const io = new IntersectionObserver((e) => { if (e[0].isIntersecting) { setShown(true); io.disconnect(); } }, { threshold: .4 });
io.observe(el); return () => io.disconnect();
}, []);
const m = String(value).match(/^([\d,]+)(.*)$/);
const [disp, setDisp] = useState(m ? "0" + (m[2] || "") : value);
useEffect(() => {
if (!shown || !m) { if (!m) setDisp(value); return; }
const target = parseInt(m[1].replace(/,/g, ""), 10); const suffix = m[2] || "";
let raf; const start = performance.now(); const dur = 1300;
const tick = (now) => {
const p = Math.min(1, (now - start) / dur); const e = 1 - Math.pow(1 - p, 3);
const cur = Math.round(target * e);
setDisp(cur.toLocaleString() + suffix);
if (p < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf);
}, [shown]);
return {disp};
}
/* ---------- Header ---------- */
function Header({ route, navigate }) {
const [scrolled, setScrolled] = useState(false);
const [open, setOpen] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12);
onScroll(); window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
useEffect(() => { setOpen(false); }, [route]);
useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; }, [open]);
const go = (e, key) => { e.preventDefault(); navigate(key); };
const active = (k) => route === k || (k === "courses" && route === "course");
return (
go(e, "home")} />
{/* mobile drawer */}
{D.NAV.map((n) => (
go(e, n.key)}
style={{ display: "flex", alignItems: "center", justifyContent: "space-between",
padding: "16px 18px", borderRadius: 16, fontWeight: 700, fontSize: "1.05rem",
background: active(n.key) ? "var(--surface)" : "transparent",
color: active(n.key) ? "var(--brand)" : "var(--ink)",
boxShadow: active(n.key) ? "var(--sh-sm)" : "none" }}>
{n.label}
))}
{D.INSTITUTE.phone}
);
}
/* ---------- Footer ---------- */
function Footer({ navigate }) {
const I = D.INSTITUTE;
const go = (e, k) => { e.preventDefault(); navigate(k); };
const socials = [["facebook", I.facebook || "#"], ["instagram", I.instagram || "#"], ["linkedin", I.linkedin || "#"], ["youtube", I.youtube || "#"]];
const quick = [["about", "About Us"], ["courses", "Courses"], ["gallery", "Gallery"], ["testimonials", "Testimonials"], ["verify", "Verify Certificate"], ["contact", "Contact"]];
const topCourses = D.COURSES.filter(c => c.featured).concat(D.COURSES.filter(c => !c.featured)).slice(0, 6);
return (
);
}
/* ---------- WhatsApp float ---------- */
function FloatActions() {
return (
);
}
Object.assign(window, { Logo, Placeholder, Stars, useReveal, CountUp, Header, Footer, FloatActions });
})();