/* ============================================================
AI Roadmap Quiz — Main App Component
Loads as type="text/babel". Reads from window.QUIZ.
============================================================ */
const { useState, useEffect, useMemo, useRef } = React;
const Q = window.QUIZ;
/* ---------- ICONS (lucide-style, stroke-based) ---------- */
const Icon = ({ name, size = 20, ...props }) => {
const s = { stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", fill: "none" };
const paths = {
arrow: <>>,
back: <>>,
};
return (
);
};
/* ---------- SPLASH SCREEN ---------- */
const SplashScreen = ({ onStart }) => (
Intro to AI
Discovery Questions
This helps us understand your organisation's current AI setup and future plans.
);
/* ---------- DETAILS SCREEN ---------- */
const DetailsScreen = ({ values, onChange, onNext, onBack, stepNum, stepTotal }) => {
const valid = !!values.employees;
return (
How many people are in the organisation?
);
};
/* ---------- QUESTION SCREEN ---------- */
const QuestionScreen = ({ q, qIndex, total, value, onChange, onNext, onBack, stepNum, stepTotal }) => {
if (q.multiSelect) {
const sel = (value && value.selected) ? value.selected : [];
const otherText = (value && value.other != null) ? value.other : '';
const noneIdx = q.options.findIndex(o => o.exclusive);
const toggle = (i) => {
if (q.options[i].exclusive) {
onChange({ selected: sel.includes(i) ? [] : [i], other: '' });
} else {
const newSel = sel.includes(i)
? sel.filter(s => s !== i)
: [...sel.filter(s => s !== noneIdx), i];
onChange({ selected: newSel, other: otherText });
}
};
const setOther = (text) => {
onChange({ selected: sel.filter(s => s !== noneIdx), other: text });
};
const valid = sel.length > 0 || otherText.trim().length > 0;
return (
{q.question}
{q.options.map((opt, i) => (
))}
{q.hasOther && (
)}
);
}
if (q.freeText) {
return (
);
}
return (
{q.question}
{q.options.map((opt, i) => (
))}
);
};
/* ---------- ATTENDEES SCREEN ---------- */
const AttendeesScreen = ({ values, onChange, onNext, stepNum, stepTotal }) => {
const extra = values.attendees && values.attendees.length ? values.attendees : [];
const updateExtra = (i, field, val) => {
const updated = extra.map((a, idx) => idx === i ? { ...a, [field]: val } : a);
onChange({ ...values, attendees: updated });
};
const addExtra = () => {
onChange({ ...values, attendees: [...extra, { name: '', role: '' }] });
};
const removeExtra = (i) => {
onChange({ ...values, attendees: extra.filter((_, idx) => idx !== i) });
};
const valid = values.company && values.name && values.role
&& values.email && /\S+@\S+\.\S+/.test(values.email);
return (
Who will be in attendance?
);
};
/* ---------- PROGRESS BAR ---------- */
const Progress = ({ current, total, sectionLabel }) => {
const pct = Math.max(0.04, current / total);
return (
);
};
/* ---------- THANK YOU SCREEN ---------- */
const ThankYouScreen = () => (
Thank you.
We look forward to your session.
);
/* ============================================================
MAIN APP
============================================================ */
/* ============================================================
EMAILJS — fill in your credentials from emailjs.com
============================================================ */
const EMAILJS_PUBLIC_KEY = "a_7LCV9VwTbMa971Y";
const EMAILJS_SERVICE_ID = "service_u6rd5qd";
const EMAILJS_TEMPLATE_ID = "template_a2i9djb";
const sendResults = (contact, details, answers) => {
const toolsAns = answers["q_tools"] || {};
const toolsSelected = (toolsAns.selected || [])
.map(i => Q.Q_TOOLS.options[i]?.label).filter(Boolean).join(", ") || "—";
const toolsOther = toolsAns.other ? `Other: ${toolsAns.other}` : "";
const toolsFull = [toolsSelected, toolsOther].filter(Boolean).join(", ");
const getLabel = (q, idx) =>
(idx !== undefined && idx !== null) ? (q.options[idx]?.label || "—") : "—";
const attendeeList = (contact.attendees || []).length > 0
? contact.attendees.map(a => `${a.name} (${a.role})`).join(", ")
: "None";
emailjs.send(EMAILJS_SERVICE_ID, EMAILJS_TEMPLATE_ID, {
company: contact.company || "—",
name: contact.name || "—",
role: contact.role || "—",
email: contact.email || "—",
attendees: attendeeList,
employees: details.employees || "—",
tools: toolsFull || "None",
q1_depth: getLabel(Q.Q1, answers[Q.Q1.id]),
q2_strategy: getLabel(Q.Q2, answers[Q.Q2.id]),
q3_data: getLabel(Q.Q3, answers[Q.Q3.id]),
q4_governance: getLabel(Q.Q4, answers[Q.Q4.id]),
q5_process: getLabel(Q.Q5, answers[Q.Q5.id]),
q6_team: getLabel(Q.Q6, answers[Q.Q6.id]),
q8_challenge: getLabel(Q.Q8, answers[Q.Q8.id]),
q_final: answers["q_final"] || "—",
}, EMAILJS_PUBLIC_KEY).catch(err => console.error("EmailJS error:", err));
};
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"theme": "light"
}/*EDITMODE-END*/;
const App = () => {
const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
const [phase, setPhase] = useState("splash"); // splash | contact | details | question | thankyou
const [qIdx, setQIdx] = useState(0);
const [details, setDetails] = useState({});
const [answers, setAnswers] = useState({});
const [contact, setContact] = useState({});
// total step count: attendees(1) + details(2) + 9 questions(3-11) = 11
const stepTotal = 11;
const currentStep = useMemo(() => {
if (phase === "contact") return 1;
if (phase === "details") return 2;
if (phase === "question") return 3 + qIdx;
return stepTotal;
}, [phase, qIdx]);
useEffect(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, [phase, qIdx]);
const advance = () => {
if (qIdx + 1 < Q.QUESTIONS.length) {
setQIdx(qIdx + 1);
} else {
sendResults(contact, details, answers);
setPhase("thankyou");
}
};
const goBack = () => {
if (phase === "details") setPhase("contact");
else if (phase === "question") {
if (qIdx === 0) setPhase("details");
else setQIdx(qIdx - 1);
}
};
const theme = tweaks.theme || "light";
return (
{phase === "splash" &&
setPhase("contact")}/>}
{phase === "contact" && (
setPhase("details")}
stepNum={1}
stepTotal={stepTotal}
/>
)}
{phase === "details" && (
{ setQIdx(0); setPhase("question"); }}
onBack={goBack}
stepNum={currentStep}
stepTotal={stepTotal}
/>
)}
{phase === "question" && (
setAnswers(prev => ({ ...prev, [Q.QUESTIONS[qIdx].id]: val }))}
onNext={advance}
onBack={goBack}
stepNum={currentStep}
stepTotal={stepTotal}
/>
)}
{phase === "thankyou" && }
{/* Tweaks Panel */}
setTweak('theme', v)}
options={[
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" },
{ value: "cardstack", label: "Cards" },
]}
/>
);
};
ReactDOM.createRoot(document.getElementById('root')).render();