import React, { useEffect, useState } from 'react'; import type { Audit } from './types'; import { fetchAudit } from './api/fetchAudit'; import { useQuiz } from './hooks/useQuiz'; import { LandingHook } from './components/LandingHook'; import { ProgressBar } from './components/ProgressBar'; import { QuestionCard } from './components/QuestionCard'; import { EmailGate } from './components/EmailGate'; import { ResultsScreen } from './components/ResultsScreen'; import { Loader2, AlertTriangle } from 'lucide-react'; import styles from './styles/quiz.module.css'; import './styles/variables.css'; const config = window.noon_audit_config; export const App: React.FC = () => { const [audit, setAudit] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const { step, currentQuestion, answers, scoreResult, totalQuestions, progress, selectAnswer, goBack, startQuiz, submitEmail, } = useQuiz(audit); // Apply accent colour from WP settings. useEffect(() => { if (config.accentColor) { document.documentElement.style.setProperty('--noon-accent', config.accentColor); // Parse Hex to RGB to set --noon-accent-rgb for shadows const hex = config.accentColor.replace(/^#/, ''); let r = 0, g = 0, b = 0; if (hex.length === 3) { r = parseInt(hex[0] + hex[0], 16); g = parseInt(hex[1] + hex[1], 16); b = parseInt(hex[2] + hex[2], 16); } else if (hex.length === 6) { r = parseInt(hex.substring(0, 2), 16); g = parseInt(hex.substring(2, 4), 16); b = parseInt(hex.substring(4, 6), 16); } if (!isNaN(r) && !isNaN(g) && !isNaN(b)) { document.documentElement.style.setProperty('--noon-accent-rgb', `${r}, ${g}, ${b}`); // Calculate hover colour (darker by ~15%) const hr = Math.max(0, Math.floor(r * 0.85)); const hg = Math.max(0, Math.floor(g * 0.85)); const hb = Math.max(0, Math.floor(b * 0.85)); const hoverHex = '#' + (1 << 24 | hr << 16 | hg << 8 | hb).toString(16).slice(1); document.documentElement.style.setProperty('--noon-accent-hover', hoverHex); } } }, []); // Fetch audit data. useEffect(() => { const slug = config.auditSlug || 'salesforce-health'; fetchAudit(slug) .then((data) => { setAudit(data); setLoading(false); }) .catch((err) => { setError(err.message); setLoading(false); }); }, []); if (loading) { return (

Loading audit…

); } if (error || !audit) { return (

{error || 'Audit not found.'}

); } const isDarkMode = config.themeMode !== 'light'; const wrapperClasses = [ styles.quizContainer, isDarkMode ? styles.darkTheme : styles.lightTheme, (isDarkMode && config.glassMode) ? 'glassMode' : '' ].filter(Boolean).join(' '); return (
{/* ── Logo Header ──────────────────────────────────────────────────── */} {config.logoUrl && (
Company Logo
)} {/* ── Landing ──────────────────────────────────────────────────────── */} {step === 'landing' && ( )} {/* ── Quiz ─────────────────────────────────────────────────────────── */} {step === 'quiz' && ( <> c.id === audit.questions[currentQuestion].categoryId )} currentAnswer={answers.find( (a) => a.questionId === audit.questions[currentQuestion].id )} onSelectAnswer={selectAnswer} onBack={goBack} showBack={currentQuestion > 0} /> )} {/* ── Email Gate ───────────────────────────────────────────────────── */} {step === 'email-gate' && scoreResult && ( )} {/* ── Results ──────────────────────────────────────────────────────── */} {step === 'results' && scoreResult && ( )} {/* ── Watermark ────────────────────────────────────────────────────── */} {config.showWatermark && ( )}
); };