> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ntop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Knowledge Check: Introduction to Fields

export const Quiz = ({title, questions}) => {
  const STORAGE_KEY = `quiz::${title ?? "default"}`;
  const load = (key, fallback) => {
    if (typeof window === "undefined") return fallback;
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return fallback;
      const s = JSON.parse(raw);
      return (key in s) ? s[key] : fallback;
    } catch {
      return fallback;
    }
  };
  const [currentQ, setCurrentQ] = useState(() => load("currentQ", 0));
  const [selected, setSelected] = useState(() => {
    const s = load("selected", []);
    return Array.isArray(s) ? s : s === null ? [] : [s];
  });
  const [inputValue, setInputValue] = useState(() => load("inputValue", ""));
  const [checked, setChecked] = useState(() => load("checked", false));
  const [answers, setAnswers] = useState(() => load("answers", []));
  const [phase, setPhase] = useState(() => {
    const p = load("phase", "idle");
    return p === "loading" ? "results" : p;
  });
  const [elapsed, setElapsed] = useState(() => load("elapsed", 0));
  const [viewingQuestions, setViewingQuestions] = useState(() => load("viewingQuestions", false));
  const timerRef = useRef(null);
  useEffect(() => {
    if (phase !== "quiz") return;
    timerRef.current = setInterval(() => setElapsed(e => e + 1), 1000);
    return () => clearInterval(timerRef.current);
  }, [phase]);
  useEffect(() => {
    if (typeof window === "undefined") return;
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify({
        currentQ,
        selected,
        inputValue,
        checked,
        answers,
        phase,
        elapsed,
        viewingQuestions
      }));
    } catch {}
  }, [currentQ, selected, inputValue, checked, answers, phase, elapsed, viewingQuestions]);
  const [isDark, setIsDark] = useState(false);
  useEffect(() => {
    const check = () => setIsDark(document.documentElement.classList.contains("dark"));
    check();
    const obs = new MutationObserver(check);
    obs.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => obs.disconnect();
  }, []);
  const optionStyle = (isCorrectOpt, isSelected, isChecked) => {
    if (!isChecked) {
      if (isSelected) return isDark ? {
        background: "rgba(59,130,246,0.2)",
        borderColor: "#60a5fa",
        borderWidth: 2
      } : {
        background: "#eff6ff",
        borderColor: "#3b82f6",
        borderWidth: 2
      };
      return isDark ? {
        background: "#1f2937",
        borderColor: "#4b5563"
      } : {
        background: "#f3f4f6",
        borderColor: "#e5e7eb"
      };
    }
    if (isCorrectOpt) return isDark ? {
      background: "rgba(34,197,94,0.15)",
      borderColor: "#22c55e"
    } : {
      background: "#dcfce7",
      borderColor: "#86efac"
    };
    if (isSelected) return isDark ? {
      background: "rgba(239,68,68,0.15)",
      borderColor: "#ef4444"
    } : {
      background: "#fee2e2",
      borderColor: "#fca5a5"
    };
    return isDark ? {
      background: "#1f2937",
      borderColor: "#4b5563"
    } : {
      background: "#f3f4f6",
      borderColor: "#e5e7eb"
    };
  };
  const RadioDot = ({selected, checked}) => {
    const dotStyle = selected && !checked ? {
      background: "#3b82f6",
      borderColor: "#3b82f6"
    } : isDark ? {
      background: "#030712",
      borderColor: "#9ca3af"
    } : {
      background: "white",
      borderColor: "#9ca3af"
    };
    return <span style={{
      ...dotStyle,
      display: "inline-block",
      width: 16,
      height: 16,
      borderRadius: "50%",
      borderWidth: 2,
      borderStyle: "solid",
      flexShrink: 0,
      position: "relative",
      boxSizing: "border-box"
    }}>
        {selected && !checked && <span style={{
      position: "absolute",
      top: "50%",
      left: "50%",
      transform: "translate(-50%,-50%)",
      width: 6,
      height: 6,
      borderRadius: "50%",
      background: "white"
    }} />}
      </span>;
  };
  const CheckboxSquare = ({selected, checked}) => {
    const boxStyle = selected && !checked ? {
      background: "#3b82f6",
      borderColor: "#3b82f6"
    } : isDark ? {
      background: "#030712",
      borderColor: "#9ca3af"
    } : {
      background: "white",
      borderColor: "#9ca3af"
    };
    return <span style={{
      ...boxStyle,
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center",
      width: 16,
      height: 16,
      borderRadius: 3,
      borderWidth: 2,
      borderStyle: "solid",
      flexShrink: 0,
      boxSizing: "border-box"
    }}>
        {selected && !checked && <span style={{
      color: "white",
      fontSize: 10,
      lineHeight: 1,
      fontWeight: "bold"
    }}>✓</span>}
      </span>;
  };
  const Btn = ({onClick, disabled, blue, children}) => {
    const btnStyle = disabled ? {
      background: isDark ? "#4b5563" : "#d1d5db",
      color: "white",
      cursor: "not-allowed"
    } : blue ? {
      background: "#3b82f6",
      color: "white",
      cursor: "pointer"
    } : isDark ? {
      background: "#f3f4f6",
      color: "#111827",
      cursor: "pointer"
    } : {
      background: "#111827",
      color: "white",
      cursor: "pointer"
    };
    return <button onClick={onClick} disabled={disabled} style={{
      ...btnStyle,
      border: "none",
      borderRadius: 999,
      padding: "10px 28px",
      fontWeight: "bold",
      fontSize: 15
    }}>
        {children}
      </button>;
  };
  const OptionRow = ({opt, isCorrectOpt, isSelected, isChecked, isMulti, onClick}) => {
    const isImageOpt = opt && typeof opt === "object" && opt.image;
    const showX = isChecked && isSelected && !isCorrectOpt;
    const showCheck = isChecked && isSelected && isCorrectOpt;
    const rightLabel = isChecked ? isCorrectOpt ? isSelected ? "Correct" : "Correct answer" : isSelected ? "Incorrect" : null : null;
    const s = optionStyle(isCorrectOpt, isSelected, isChecked);
    return <div onClick={onClick} style={{
      ...s,
      display: "flex",
      alignItems: isImageOpt ? "flex-start" : "center",
      justifyContent: "space-between",
      padding: "12px 16px",
      marginBottom: 8,
      borderRadius: 8,
      borderStyle: "solid",
      cursor: isChecked ? "default" : "pointer",
      userSelect: "none"
    }}>
        <div style={{
      display: "flex",
      alignItems: isImageOpt ? "flex-start" : "center",
      gap: 10,
      flex: 1
    }}>
          {showX && <span style={{
      color: "#ef4444",
      fontWeight: "bold",
      fontSize: 14,
      flexShrink: 0
    }}>✕</span>}
          {showCheck && <span style={{
      color: "#22c55e",
      fontWeight: "bold",
      fontSize: 14,
      flexShrink: 0
    }}>✓</span>}
          {isMulti ? <CheckboxSquare selected={isSelected} checked={isChecked} /> : <RadioDot selected={isSelected} checked={isChecked} />}
          {isImageOpt ? <div style={{
      flex: 1
    }}>
              <img src={opt.image} alt={opt.label || ""} style={{
      width: "100%",
      borderRadius: 4,
      display: "block"
    }} />
              {opt.label && <span style={{
      fontSize: 14,
      marginTop: 6,
      display: "block"
    }}>{opt.label}</span>}
            </div> : <span style={{
      fontSize: 15
    }}>{opt}</span>}
        </div>
        {rightLabel && <span style={{
      fontSize: 13,
      color: isDark ? "#9ca3af" : "#6b7280",
      marginLeft: 16,
      whiteSpace: "nowrap",
      flexShrink: 0
    }}>
            {rightLabel}
          </span>}
      </div>;
  };
  const InputField = ({value, onChange, isChecked, isCorrect}) => {
    const borderColor = isChecked ? isCorrect ? "#86efac" : "#fca5a5" : isDark ? "#4b5563" : "#e5e7eb";
    const bgColor = isChecked ? isCorrect ? isDark ? "rgba(34,197,94,0.15)" : "#dcfce7" : isDark ? "rgba(239,68,68,0.15)" : "#fee2e2" : isDark ? "#1f2937" : "#f3f4f6";
    return <div>
        {isChecked && <div style={{
      display: "flex",
      justifyContent: "space-between",
      marginBottom: 4
    }}>
            <span style={{
      color: isCorrect ? "#22c55e" : "#ef4444",
      fontWeight: "bold",
      fontSize: 14
    }}>
              {isCorrect ? "✓" : "✕"}
            </span>
            <span style={{
      fontSize: 13,
      color: isDark ? "#9ca3af" : "#6b7280"
    }}>
              {isCorrect ? "Correct" : "Incorrect"}
            </span>
          </div>}
        <input type="text" value={value} onChange={e => onChange(e.target.value)} disabled={isChecked} placeholder="" style={{
      width: "100%",
      padding: "10px 12px",
      borderRadius: 8,
      border: `1px solid ${borderColor}`,
      background: bgColor,
      fontSize: 15,
      boxSizing: "border-box",
      outline: "none",
      color: isDark ? "#f9fafb" : "#111827"
    }} />
      </div>;
  };
  const formatTime = secs => {
    const h = String(Math.floor(secs / 3600)).padStart(2, "0");
    const m = String(Math.floor(secs % 3600 / 60)).padStart(2, "0");
    const s = String(secs % 60).padStart(2, "0");
    return `${h}:${m}:${s}`;
  };
  const isAnswerCorrect = (ans, q) => {
    if (q.type === "input") {
      const accepted = Array.isArray(q.correct) ? q.correct : [q.correct];
      return typeof ans === "string" && accepted.some(c => ans.trim().toLowerCase() === c.trim().toLowerCase());
    }
    if (Array.isArray(q.correct)) {
      return [...ans].sort().join(",") === [...q.correct].sort().join(",");
    }
    return ans[0] === q.correct;
  };
  const correctAnswerLabel = q => {
    if (q.type !== "input") return null;
    return Array.isArray(q.correct) ? q.correct.join(", ") : q.correct;
  };
  const question = questions[currentQ];
  const isLastQ = currentQ === questions.length - 1;
  const isInputQ = question.type === "input";
  const isMulti = !isInputQ && Array.isArray(question.correct);
  const currentAnswer = isInputQ ? inputValue : selected;
  const isCorrect = isAnswerCorrect(currentAnswer, question);
  const handleCheck = () => {
    if (isInputQ ? inputValue.trim() : selected.length > 0) {
      setChecked(true);
    }
  };
  const handleNext = () => {
    const finalAnswers = [...answers, currentAnswer];
    setAnswers(finalAnswers);
    if (isLastQ) {
      setPhase("loading");
      setTimeout(() => setPhase("results"), 2000);
    } else {
      setCurrentQ(q => q + 1);
      setSelected([]);
      setInputValue("");
      setChecked(false);
    }
  };
  const handleRestart = () => {
    if (typeof window !== "undefined") localStorage.removeItem(STORAGE_KEY);
    setCurrentQ(0);
    setSelected([]);
    setInputValue("");
    setChecked(false);
    setAnswers([]);
    setPhase("idle");
    setElapsed(0);
    setViewingQuestions(false);
  };
  if (phase === "idle") {
    return <Btn onClick={() => setPhase("quiz")}>Start Quiz</Btn>;
  }
  if (phase === "loading") {
    return <div style={{
      background: isDark ? "#1f2937" : "#f3f4f6",
      borderRadius: 8,
      padding: "20px 24px"
    }}>
        <style>{`@keyframes quiz-progress { from { width: 10%; } to { width: 100%; } }`}</style>
        <p className="font-bold text-base mb-1.5">Results</p>
        <p className="font-bold mb-3">Quiz complete. Results are being recorded.</p>
        <div style={{
      background: isDark ? "#374151" : "#e5e7eb",
      borderRadius: 999,
      height: 6,
      overflow: "hidden"
    }}>
          <div style={{
      background: "#3b82f6",
      height: "100%",
      borderRadius: 999,
      animation: "quiz-progress 2s ease-in-out forwards"
    }} />
        </div>
      </div>;
  }
  if (phase === "results") {
    const score = answers.filter((ans, i) => isAnswerCorrect(ans, questions[i])).length;
    const pct = Math.round(score / questions.length * 100);
    if (viewingQuestions) {
      return <div>
          {questions.map((q, qi) => <div key={qi} className="mb-6">
              <p className="font-bold mb-2.5">
                {qi + 1}. {q.text}
              </p>
              {q.type === "input" ? <div>
                  <div style={{
        display: "flex",
        justifyContent: "space-between",
        marginBottom: 4
      }}>
                    <span style={{
        color: isAnswerCorrect(answers[qi], q) ? "#22c55e" : "#ef4444",
        fontWeight: "bold",
        fontSize: 14
      }}>
                      {isAnswerCorrect(answers[qi], q) ? "✓" : "✕"}
                    </span>
                    <span style={{
        fontSize: 13,
        color: isDark ? "#9ca3af" : "#6b7280"
      }}>
                      {isAnswerCorrect(answers[qi], q) ? "Correct" : "Incorrect"}
                    </span>
                  </div>
                  <input type="text" value={answers[qi] ?? ""} disabled style={{
        width: "100%",
        padding: "10px 12px",
        borderRadius: 8,
        border: `1px solid ${isAnswerCorrect(answers[qi], q) ? "#86efac" : "#fca5a5"}`,
        background: isAnswerCorrect(answers[qi], q) ? isDark ? "rgba(34,197,94,0.15)" : "#dcfce7" : isDark ? "rgba(239,68,68,0.15)" : "#fee2e2",
        fontSize: 15,
        boxSizing: "border-box",
        color: isDark ? "#f9fafb" : "#111827"
      }} />
                  {!isAnswerCorrect(answers[qi], q) && <p style={{
        fontSize: 14,
        marginTop: 6,
        color: isDark ? "#d1d5db" : "#374151"
      }}>
                      Incorrect, Correct Answer: {correctAnswerLabel(q)}
                    </p>}
                </div> : q.options.map((opt, i) => <OptionRow key={i} opt={opt} isCorrectOpt={Array.isArray(q.correct) ? q.correct.includes(i) : i === q.correct} isSelected={Array.isArray(answers[qi]) ? answers[qi]?.includes(i) ?? false : false} isChecked={true} isMulti={Array.isArray(q.correct)} onClick={() => {}} />)}
            </div>)}
          <div className="flex justify-center gap-3 mt-2">
            <Btn onClick={() => setViewingQuestions(false)}>Back to Results</Btn>
            <Btn onClick={handleRestart} blue>
              Restart Quiz
            </Btn>
          </div>
        </div>;
    }
    return <div>
        <p className="font-bold text-base mb-1">
          {score} of {questions.length} Questions answered correctly
        </p>
        <p style={{
      color: isDark ? "#9ca3af" : "#6b7280"
    }} className="mb-4">
          Your time: {formatTime(elapsed)}
        </p>
        <div style={{
      background: isDark ? "rgba(59,130,246,0.1)" : "#eff6ff",
      borderRadius: 8,
      padding: "16px 20px",
      textAlign: "center",
      marginBottom: 8
    }}>
          <p className="font-bold m-0">
            You have reached {score} of {questions.length} point(s), ({pct}%)
          </p>
        </div>
        <div style={{
      height: 1,
      background: isDark ? "#374151" : "#e5e7eb",
      margin: "20px 0"
    }} />
        <div className="flex justify-center gap-3">
          <Btn onClick={() => setViewingQuestions(true)}>View Questions</Btn>
          <Btn onClick={handleRestart} blue>
            Restart Quiz
          </Btn>
        </div>
      </div>;
  }
  return <div className="max-w-2xl">
      <p className="mb-3">{question.text}</p>
      {question.image && <img src={question.image} alt="" style={{
    width: "100%",
    borderRadius: 8,
    marginBottom: 16
  }} />}
      <br />

      {isInputQ ? <div>
          <InputField value={inputValue} onChange={val => {
    if (!checked) setInputValue(val);
  }} isChecked={checked} isCorrect={isCorrect} />
          {checked && !isCorrect && <p style={{
    fontSize: 14,
    marginTop: 6,
    color: isDark ? "#d1d5db" : "#374151"
  }}>
              Incorrect, Correct Answer: {correctAnswerLabel(question)}
            </p>}
        </div> : question.options.map((opt, i) => <OptionRow key={i} opt={opt} isCorrectOpt={isMulti ? question.correct.includes(i) : i === question.correct} isSelected={selected.includes(i)} isChecked={checked} isMulti={isMulti} onClick={() => {
    if (checked) return;
    if (isMulti) {
      setSelected(prev => prev.includes(i) ? prev.filter(x => x !== i) : [...prev, i]);
    } else {
      setSelected([i]);
    }
  }} />)}

      {checked && <div style={{
    background: isDark ? "#1f2937" : "#f9fafb",
    border: `1px solid ${isDark ? "#4b5563" : "#e5e7eb"}`,
    borderRadius: 8,
    padding: "14px 16px",
    marginTop: 8
  }}>
          {isCorrect ? <div className="flex flex-col gap-1">
              <span style={{
    color: isDark ? "#4ade80" : "#16a34a",
    fontWeight: "bold"
  }}>Correct</span>
              <span style={{
    fontWeight: "bold"
  }}>{question.explanation}</span>
            </div> : <div className="flex flex-col gap-1">
              <span style={{
    color: "#ef4444",
    fontWeight: "bold"
  }}>Incorrect</span>
              <span style={{
    fontWeight: "bold"
  }}>{question.explanation}</span>
            </div>}
        </div>}

      <div className="flex justify-end mt-4">
        {!checked ? <Btn onClick={handleCheck} disabled={isInputQ ? !inputValue.trim() : selected.length === 0}>
            Check
          </Btn> : <Btn onClick={handleNext}>{isLastQ ? "Finish Quiz" : "Next"}</Btn>}
      </div>
    </div>;
};

<Quiz />
