// Atlas · Step 2 — Scope review with themed question groups
// ── Module-scope utilities (must be outside component to avoid remount on every render) ──
function autoResize(el) {
if (!el) return;
el.style.height = "auto";
el.style.height = el.scrollHeight + "px";
}
const _scopeRowRef = (el) => { if (el) autoResize(el); };
function ScopeRow({ prefix, value, onChange, onDelete, placeholder }) {
return (
{readOnly && (
This report has been researched. The scope is view-only.
)}
{/* Review verdict */}
{saving
? Re-reviewing…
: displayMsg || (liveApproved ? "Scope approved." : "Revisions needed.")}
{new Date().toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" })}
{/* Type recommendation advisory */}
{liveApproved && recType && typeDecision === null && (
💡
Report type suggestion:{" "}
The review agent recommends switching to {TYPE_LABELS[recType] || recType}.{" "}
{recReason}
)}
{liveApproved && recType && typeDecision === "accept" && (
Report type will be set to {TYPE_LABELS[recType] || recType}.
{recType === "position" && (
Your company or technology *
{ setAnchorEntity(e.target.value); setAnchorEntitySaved(false); setAnchorWarning(null); }}
onKeyDown={e => { if (e.key === "Enter" && anchorEntity.trim().length >= 2) { e.preventDefault(); handleSave(); } }}
style={{ flex:1, fontSize:13, padding:"7px 10px", borderRadius:6, border:"1px solid var(--line-2)", background:"var(--bg)", color:"var(--ink)", boxSizing:"border-box" }}
/>
{anchorEntitySaved && anchorWarning && (
⚠ {anchorWarning} — you can still proceed or change the entity.
)}
{anchorEntitySaved && !anchorWarning && (
✓ Re-assessed with {anchorEntity.trim()}. You can now generate the outline.
)}
{!anchorEntitySaved && (
Enter your entity and click Confirm — the scope agent will re-assess before moving to the outline.
)}
{anchorChangedWarning && (
⚠ {anchorChangedWarning}
)}
)}
)}
{/* Exec summary — only shown when approved */}
{liveApproved &&
{title}
{summary &&
{summary}
}
{qGroups.length > 0 && (() => {
const totalQ = qGroups.reduce((s, g) => s + g.questions.length, 0);
const themes = qGroups.map(g => g.theme).filter(Boolean);
const themeStr = themes.length > 1
? themes.slice(0, -1).join(", ") + " and " + themes[themes.length - 1]
: themes[0] || null;
return (
The report will address {totalQ} research question{totalQ !== 1 ? "s" : ""}
{themeStr ? <> structured around {themeStr}> : ""}.
);
})()}
}
{liveApproved &&
{/* ── Research Questions ── */}
Research Questions
{qGroups.map((group, gi) => (
updateQTheme(gi, val)}
onDelete={() => deleteQGroup(gi)}
showDelete={multiQGroups}
/>
{group.questions.map((q, qi) => (
updateQ(gi, qi, val)}
onDelete={() => deleteQ(gi, qi)}
placeholder="Research question…"
/>
))}
))}
{/* ── Parameters ── */}
{(totalParams > 0 || pGroups.length > 0) && (
{state.reportType === "scout" ? "Fields to capture per entry" :
state.reportType === "company" ? "Fields to capture per company" :
"Capture Parameters"}
{multiPGroups && (
)}
{pGroups.map((group, gi) => (
{(multiPGroups || group.group) && (
updatePGroup(gi, val)}
onDelete={() => deletePGroup(gi)}
showDelete={multiPGroups}
/>
)}
{group.items.map((item, pi) => (
updateP(gi, pi, val)}
onDelete={() => deleteP(gi, pi)}
placeholder="Field to capture…"
/>
))}
))}
{!multiPGroups && (
)}
)}
{/* ── Exclusions ── */}
{showExcl ? (
{exclusions.length === 0 && (
None — click + Add to add one
)}
{exclusions.map((item, i) => (
updateExcl(i, val)} onDelete={() => deleteExcl(i)} placeholder="Out of scope item…" />
))}
) : (
)}
}
{/* Action bar — save state integrated */}
{readOnly
? View only
: Scope written to scope.md}
{readOnly &&
}
{!readOnly && (dirty ? (
Your edits will be sent to the review agent for re-approval.
{saveError && {saveError}}
) : (
))}
{undoItem && (
Removed {undoItem.label}
)}
);
}
window.StepScope = StepScope;