// Shared layout components: header, footer, ticker, placeholders, lang state
const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;
// Language context (mock — toggles copy between IT and EN where we bother)
const LangCtx = createContext({ lang: 'it', setLang: () => {} });
function useLang() {
return useContext(LangCtx);
}
function t(it, en) {
const { lang } = useLang();
return lang === 'en' ? en : it;
}
function T({ it, en }) {
const { lang } = useLang();
return lang === 'en' ? en : it;
}
function LangProvider({ children }) {
const [lang, setLang] = useState(() => {
try { return localStorage.getItem('geek_lang') || 'it'; } catch { return 'it'; }
});
useEffect(() => {
try { localStorage.setItem('geek_lang', lang); } catch {}
document.documentElement.lang = lang;
}, [lang]);
return {children};
}
function LangToggle() {
const { lang, setLang } = useLang();
return (
);
}
function SiteHeader({ current }) {
const items = [
{ id: 'home', href: 'index.html', it: 'Home', en: 'Home' },
{ id: 'servizi', href: 'servizi.html', it: 'Servizi', en: 'Services' },
{ id: 'progetti', href: 'progetti.html', it: 'Progetti', en: 'Work' },
{ id: 'metodo', href: 'metodo.html', it: 'Metodo', en: 'Method' },
{ id: 'blog', href: 'blog.html', it: 'Blog', en: 'Journal' },
{ id: 'contatti', href: 'contatti.html', it: 'Contatti', en: 'Contact' },
];
const [menuOpen, setMenuOpen] = useState(false);
const close = () => setMenuOpen(false);
useEffect(() => {
if (!menuOpen) return;
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
const onKey = (e) => { if (e.key === 'Escape') close(); };
window.addEventListener('keydown', onKey);
return () => {
document.body.style.overflow = prev;
window.removeEventListener('keydown', onKey);
};
}, [menuOpen]);
return (
);
}
function Ticker({ items }) {
const defaultItems = [
'SOFTWARE GESTIONALE SU MISURA',
'WEBSERVICES REST / SOAP',
'AGENTI AI PER PROCESSI AZIENDALI',
'INTEGRAZIONE SISTEMI LEGACY',
'KPI MISURABILI, NON PROMESSE',
'PMI ITALIANE • 10–80 DIPENDENTI',
];
const it = items || defaultItems;
const doubled = [...it, ...it];
return (
{doubled.map((s, i) => (
{s}
))}
);
}
function SiteFooter() {
return (
);
}
const NEWSLETTER_ENDPOINT = 'server/api/newsletter.php';
function NewsletterForm({ source = 'web', compact = false }) {
const { lang } = useLang();
const [email, setEmail] = useState('');
const [website, setWebsite] = useState(''); // honeypot
const [busy, setBusy] = useState(false);
const [state, setState] = useState(null); // 'ok' | 'already' | 'error'
const [errorMsg, setErrorMsg] = useState('');
const onSubmit = async (e) => {
e.preventDefault();
if (busy) return;
setBusy(true);
setState(null);
setErrorMsg('');
try {
const res = await fetch(NEWSLETTER_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, lang, source, website }),
});
const data = await res.json().catch(() => ({}));
if (res.ok && data && data.ok) {
setState(data.status === 'already' ? 'already' : 'ok');
setEmail('');
} else {
setState('error');
setErrorMsg((data && data.error) || (lang === 'en' ? 'Something went wrong.' : 'Qualcosa è andato storto.'));
}
} catch (err) {
setState('error');
setErrorMsg(lang === 'en' ? 'Network error.' : 'Errore di rete.');
} finally {
setBusy(false);
}
};
if (state === 'ok') {
return (
);
}
if (state === 'already') {
return (
);
}
return (
);
}
function Placeholder({ label, aspect = '4/3', style = {}, children }) {
return (
);
}
// Expose globally
Object.assign(window, { LangCtx, LangProvider, LangToggle, SiteHeader, SiteFooter, Ticker, Placeholder, NewsletterForm, useLang, T, t });