| 777 | } |
| 778 | |
| 779 | function tasksTab(s) { |
| 780 | let html = ""; |
| 781 | // Custom skills |
| 782 | html += '<div class="card"><h2>Custom skills <span class="muted" style="font-weight:400">.github/skills</span></h2>'; |
| 783 | if (!s.skills || !s.skills.length) { |
| 784 | html += '<p class="muted" style="margin:0 0 10px">No custom skills found. Capture an org-specific migration as a reusable skill.</p>' + |
| 785 | '<div class="actions">' + btn("Create a custom skill", "create_skill") + "</div>"; |
| 786 | } else { |
| 787 | s.skills.forEach((sk) => { |
| 788 | html += |
| 789 | '<div class="task"><div class="body"><div class="name">' + esc(sk.name) + "</div>" + |
| 790 | '<div class="sum">' + esc(sk.description || sk.folder) + "</div></div>" + |
| 791 | '<div>' + btn("Run", "run_skill", { folder: sk.folder }, { primary: true }) + "</div></div>"; |
| 792 | }); |
| 793 | } |
| 794 | html += "</div>"; |
| 795 | |
| 796 | // Predefined catalog grouped by category, relevant first |
| 797 | const tasks = (s.tasks || []).slice().sort((a, b) => (b.relevant ? 1 : 0) - (a.relevant ? 1 : 0)); |
| 798 | const cats = {}; |
| 799 | tasks.forEach((t) => { |
| 800 | (cats[t.category] = cats[t.category] || []).push(t); |
| 801 | }); |
| 802 | html += '<div class="card"><h2>Microsoft predefined tasks</h2>'; |
| 803 | Object.keys(cats).forEach((cat) => { |
| 804 | html += '<div class="cat">' + esc(cat) + "</div>"; |
| 805 | cats[cat].forEach((t) => (html += taskRow(t))); |
| 806 | }); |
| 807 | html += "</div>"; |
| 808 | return html; |
| 809 | } |
| 810 | |
| 811 | function summaryTab(s) { |
| 812 | let html = ""; |