(s)
| 596 | } |
| 597 | |
| 598 | function planTab(s) { |
| 599 | const usingProgress = !!(s.progress && s.progress.steps.length); |
| 600 | const steps = (usingProgress ? s.progress.steps : (s.plan ? s.plan.steps : [])) || []; |
| 601 | if (!steps.length) { |
| 602 | return emptyState( |
| 603 | "No plan yet", |
| 604 | "Run an assessment or generate an upgrade plan. App Modernization writes <code>plan.md</code> and a <code>progress.md</code> checklist, and your steps land here — each unchecked one becomes a button that hands that exact step to the agent.", |
| 605 | btn("Run assessment", "start_assessment", {}, { primary: true }) + btn("Generate upgrade plan", "generate_plan", { targetJava: 21 }) |
| 606 | ); |
| 607 | } |
| 608 | const src = usingProgress ? "progress.md" : "plan.md"; |
| 609 | const total = steps.length; |
| 610 | const done = steps.filter((x) => x.status === "done").length; |
| 611 | const ord = s.ordering || { activeRank: null, activePhase: null }; |
| 612 | |
| 613 | // Intro + overall progress + legend. |
| 614 | let html = '<div class="card"><h2>Plan & Progress <span class="muted" style="font-weight:400">' + esc(src) + "</span><span class=\"spacer\"></span>" + statusBadge(s.status) + "</h2>"; |
| 615 | html += '<p class="muted" style="margin:0 0 10px">Your live modernization checklist. Each unchecked step has a <b>Work on this</b> button that hands just that step to the agent in this session — it checks the box here when the step is done.</p>'; |
| 616 | html += '<div class="progress"><span style="width:' + (s.percent || 0) + '%"></span></div>'; |
| 617 | html += '<p class="muted" style="margin:6px 0 0">' + done + " of " + total + " steps done · " + (s.percent || 0) + "% complete</p>"; |
| 618 | html += '<div class="legend"><span><span class="dot done"></span>Done</span><span><span class="dot in_progress"></span>In progress</span><span><span class="dot pending"></span>To do</span><span>🔒 Do later</span></div>'; |
| 619 | html += "</div>"; |
| 620 | |
| 621 | // Recommended-order guide so users don't work steps out of sequence. |
| 622 | html += approachGuide(ord); |
| 623 | |
| 624 | // Autopilot: hand the whole phase to the agent instead of clicking each step. |
| 625 | html += autopilotCard(s); |
| 626 | |
| 627 | // "Continue here" — the recommended next step: first not-done step in the |
| 628 | // active phase (falls back to the first not-done step overall). |
| 629 | const next = steps.find((x) => x.status !== "done" && (ord.activeRank == null || x.rank === ord.activeRank)) || steps.find((x) => x.status !== "done"); |
| 630 | if (next) { |
| 631 | html += |
| 632 | '<div class="hero"><div class="eyebrow">Continue here · recommended next step</div>' + |
| 633 | '<div class="htitle">' + esc(next.title) + "</div>" + |
| 634 | (next.section ? '<div class="hbody">Phase: ' + esc(next.section) + "</div>" : "") + |
| 635 | '<div class="actions">' + stepAction(next) + (next.section && /validation|gates/i.test(next.section) ? btn("Open Validation", "goto:validation") : "") + "</div></div>"; |
| 636 | } else { |
| 637 | html += '<div class="banner">✓ Every step is checked off. Re-run the validation gates, then open a pull request.</div>'; |
| 638 | } |
| 639 | |
| 640 | // Steps grouped by their phase/section. |
| 641 | const groups = []; |
| 642 | const idx = {}; |
| 643 | steps.forEach((st) => { |
| 644 | const key = st.section || "Steps"; |
| 645 | if (idx[key] == null) { idx[key] = groups.length; groups.push({ name: key, items: [], rank: st.rank }); } |
| 646 | groups[idx[key]].items.push(st); |
| 647 | }); |
| 648 | |
| 649 | groups.forEach((g) => { |
| 650 | const gdone = g.items.filter((x) => x.status === "done").length; |
| 651 | const isValidation = /validation|gates/i.test(g.name); |
| 652 | const gLocked = ord.activeRank != null && g.rank != null && g.rank > ord.activeRank; |
| 653 | html += '<div class="card' + (gLocked ? " locked" : "") + '"><h2>' + (gLocked ? "🔒 " : "") + esc(g.name) + ' <span class="badge ' + (gdone === g.items.length && g.items.length ? "b-green" : "b-gray") + '">' + gdone + "/" + g.items.length + "</span>"; |
| 654 | if (isValidation) html += '<span class="spacer"></span>' + btn("Open Validation →", "goto:validation"); |
| 655 | else if (gLocked) html += '<span class="spacer"></span><span class="smuted">after ' + esc(shortPhase(ord.activePhase)) + "</span>"; |
nothing calls this directly
no test coverage detected