| 253 | // ---- tab renderers ------------------------------------------------------- |
| 254 | |
| 255 | function overviewTab(s) { |
| 256 | let html = ""; |
| 257 | |
| 258 | // Environment readiness nudge — surfaces a blocker before the user starts. |
| 259 | const d = s.doctor; |
| 260 | if (d && d.overall === "blocked") { |
| 261 | html += '<div class="banner banner-red">⚠ Your environment isn\'t ready — a required tool is missing. ' + btn("Open Readiness →", "goto:readiness") + "</div>"; |
| 262 | } else if (d && d.overall === "caution") { |
| 263 | html += '<div class="banner">Some environment items need a look before you go further. ' + btn("Open Readiness →", "goto:readiness") + "</div>"; |
| 264 | } |
| 265 | |
| 266 | // Journey stepper — always shows where you are in the workflow. |
| 267 | html += '<div class="card"><h2>Your modernization journey</h2>' + journeyHtml(s) + "</div>"; |
| 268 | |
| 269 | // One adaptive "do this next" hero so the next click is never ambiguous. |
| 270 | const hero = heroNext(s); |
| 271 | html += |
| 272 | '<div class="hero"><div class="eyebrow">' + esc(hero.eyebrow) + "</div>" + |
| 273 | '<div class="htitle">' + esc(hero.title) + "</div>" + |
| 274 | '<div class="hbody">' + hero.body + "</div>" + |
| 275 | '<div class="actions">' + hero.actions + "</div></div>"; |
| 276 | |
| 277 | // Autopilot nudge — once there's a checklist with work left, offer to run it |
| 278 | // hands-free. Hidden while a run is already active (the strip covers that). |
| 279 | const planSteps = (s.progress && s.progress.steps.length ? s.progress.steps : (s.plan ? s.plan.steps : [])) || []; |
| 280 | const pendingSteps = planSteps.filter((x) => x.status !== "done").length; |
| 281 | const envBlocked = d && d.overall === "blocked"; |
| 282 | if (pendingSteps > 0 && !(s.autopilot && s.autopilot.running)) { |
| 283 | const cta = envBlocked |
| 284 | ? btn("Open Readiness →", "goto:readiness") |
| 285 | : btn("▶ Run on autopilot", "autopilot_start", { scope: "phase" }, { primary: true }) + btn("See in Plan →", "goto:plan"); |
| 286 | html += |
| 287 | '<div class="card autopilot"><h2>⚡ Run on autopilot <span class="badge b-blue">beta</span></h2>' + |
| 288 | '<p class="muted" style="margin:0 0 10px">Instead of clicking each step, let Copilot work the ' + pendingSteps + |
| 289 | " remaining step(s) in order and update this dashboard live. It pauses at the end of the phase and does not commit.</p>" + |
| 290 | '<div class="actions">' + cta + "</div></div>"; |
| 291 | } |
| 292 | |
| 293 | // Findings snapshot (only once an assessment has been run). |
| 294 | const rep = s.report; |
| 295 | if (rep && rep.findings && rep.findings.length) { |
| 296 | const by = sevCounts(rep.findings); |
| 297 | html += '<div class="card"><h2>Assessment findings <span class="spacer"></span>' + btn("Open Assessment →", "goto:assessment") + "</h2>"; |
| 298 | if (rep.summary) html += '<p class="muted" style="margin:0 0 10px">' + esc(rep.summary) + "</p>"; |
| 299 | html += '<div class="sevrow">' + sevChip("P0", by.P0) + sevChip("P1", by.P1) + sevChip("P2", by.P2) + sevChip("P3", by.P3) + "</div></div>"; |
| 300 | } |
| 301 | |
| 302 | // Stack facts. |
| 303 | const a = s.assessment || {}; |
| 304 | const jv = a.javaVersion ? "Java " + a.javaVersion : "Java (unknown)"; |
| 305 | html += |
| 306 | '<div class="card"><h2>Stack</h2><div class="grid">' + |
| 307 | kv("Build tool", a.buildTool || "—") + |
| 308 | kv("Java version", esc(jv)) + |
| 309 | kv("Spring Boot", a.springBoot ? "Yes" + (a.springVersion ? " " + esc(a.springVersion) : "") : "No") + |
| 310 | kv("Container", a.hasDockerfile ? "Dockerfile ✓" : "None") + |
| 311 | kv("Git branch", s.git && s.git.branch ? esc(s.git.branch) : "—") + |
| 312 | kv("Working tree", s.git ? (s.git.dirty ? s.git.changedFiles + " changed" : "clean") : "—") + |