Spaces:
Sleeping
Sleeping
| import React from "react"; | |
| // One graded row: a PASS/FAIL toggle (kind="verdict") or a numeric count input | |
| // (kind="count"), plus a rationale textarea. Used by EventCard, VideoLevelAxes, | |
| // and the Training page's qualification quiz - the single place PASS/FAIL and | |
| // count UI is implemented. | |
| // | |
| // Rationale is always optional (never blocks saving/continuing) - it's | |
| // strongly encouraged on real videos, especially for FAIL, but not required. | |
| export default function AxisItemRow({ | |
| axisKey, | |
| title, | |
| summary, | |
| kind, | |
| value, | |
| rationale, | |
| onChangeValue, | |
| onChangeRationale, | |
| isPractice = false, | |
| grading | |
| }) { | |
| const showGrade = grading && grading.checked; | |
| return ( | |
| <div className="axis-item-row"> | |
| <div className="axis-item-row-header"> | |
| <span className="axis">{axisKey}</span> | |
| <span className="axis-item-title">{title}</span> | |
| {showGrade && ( | |
| <span className={`grade-mark ${grading.correct ? "ok" : "bad"}`}> | |
| {grading.correct ? "✓ correct" : "✗ incorrect"} | |
| </span> | |
| )} | |
| </div> | |
| {summary && <p className="axis-item-summary muted">{summary}</p>} | |
| {kind === "count" ? ( | |
| <label className="count-field"> | |
| Observed count | |
| <input | |
| type="number" | |
| min={0} | |
| value={value} | |
| onChange={(e) => onChangeValue(e.target.value)} | |
| /> | |
| </label> | |
| ) : ( | |
| <div className="verdict-toggle"> | |
| {["PASS", "FAIL"].map((option) => ( | |
| <button | |
| key={option} | |
| type="button" | |
| className={`verdict-btn ${option === "PASS" ? "pass-option" : "fail-option"} ${ | |
| value === option ? "selected" : "" | |
| }`} | |
| onClick={() => onChangeValue(option)} | |
| > | |
| {option} | |
| </button> | |
| ))} | |
| </div> | |
| )} | |
| <textarea | |
| className="rationale-input" | |
| placeholder={ | |
| isPractice ? "Rationale (optional for practice)" : "Rationale (optional, but encouraged — especially for FAIL)" | |
| } | |
| value={rationale} | |
| onChange={(e) => onChangeRationale(e.target.value)} | |
| /> | |
| {showGrade && !grading.correct && grading.explanation && ( | |
| <div className="explanation-box">{grading.explanation}</div> | |
| )} | |
| </div> | |
| ); | |
| } | |