@sienklogic/plan-build-run 2.26.2 → 2.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +29 -0
  3. package/dashboard/public/css/layout.css +7 -283
  4. package/dashboard/public/css/status-colors.css +7 -0
  5. package/dashboard/public/css/tokens.css +3 -3
  6. package/dashboard/public/js/sidebar-toggle.js +9 -31
  7. package/dashboard/public/js/theme-toggle.js +4 -4
  8. package/dashboard/src/views/partials/activity-feed.ejs +17 -9
  9. package/dashboard/src/views/partials/analytics-content.ejs +178 -88
  10. package/dashboard/src/views/partials/audit-detail-content.ejs +6 -4
  11. package/dashboard/src/views/partials/audits-content.ejs +28 -26
  12. package/dashboard/src/views/partials/breadcrumbs.ejs +8 -4
  13. package/dashboard/src/views/partials/config-content.ejs +98 -95
  14. package/dashboard/src/views/partials/dashboard-content.ejs +69 -60
  15. package/dashboard/src/views/partials/dependencies-content.ejs +5 -3
  16. package/dashboard/src/views/partials/empty-state.ejs +10 -5
  17. package/dashboard/src/views/partials/footer.ejs +8 -2
  18. package/dashboard/src/views/partials/head.ejs +2 -1
  19. package/dashboard/src/views/partials/header.ejs +16 -19
  20. package/dashboard/src/views/partials/layout-bottom.ejs +5 -40
  21. package/dashboard/src/views/partials/layout-top.ejs +6 -5
  22. package/dashboard/src/views/partials/logs-content.ejs +26 -29
  23. package/dashboard/src/views/partials/milestone-detail-content.ejs +5 -5
  24. package/dashboard/src/views/partials/milestones-content.ejs +40 -31
  25. package/dashboard/src/views/partials/note-detail-content.ejs +7 -5
  26. package/dashboard/src/views/partials/notes-content.ejs +4 -4
  27. package/dashboard/src/views/partials/phase-content.ejs +6 -8
  28. package/dashboard/src/views/partials/phase-doc-content.ejs +13 -15
  29. package/dashboard/src/views/partials/phase-timeline.ejs +22 -15
  30. package/dashboard/src/views/partials/phases-content.ejs +98 -84
  31. package/dashboard/src/views/partials/quick-content.ejs +34 -32
  32. package/dashboard/src/views/partials/quick-detail-content.ejs +20 -19
  33. package/dashboard/src/views/partials/requirements-content.ejs +6 -6
  34. package/dashboard/src/views/partials/research-content.ejs +14 -14
  35. package/dashboard/src/views/partials/research-detail-content.ejs +13 -11
  36. package/dashboard/src/views/partials/roadmap-content.ejs +145 -128
  37. package/dashboard/src/views/partials/sidebar.ejs +86 -140
  38. package/dashboard/src/views/partials/todo-create-content.ejs +51 -46
  39. package/dashboard/src/views/partials/todo-detail-content.ejs +26 -25
  40. package/dashboard/src/views/partials/todos-content.ejs +65 -62
  41. package/dashboard/src/views/partials/todos-done-content.ejs +33 -31
  42. package/package.json +1 -1
  43. package/plugins/copilot-pbr/plugin.json +1 -1
  44. package/plugins/copilot-pbr/skills/build/SKILL.md +12 -0
  45. package/plugins/copilot-pbr/skills/quick/SKILL.md +12 -0
  46. package/plugins/copilot-pbr/skills/review/SKILL.md +14 -0
  47. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
  48. package/plugins/cursor-pbr/README.md +20 -0
  49. package/plugins/cursor-pbr/skills/build/SKILL.md +12 -0
  50. package/plugins/cursor-pbr/skills/quick/SKILL.md +12 -0
  51. package/plugins/cursor-pbr/skills/review/SKILL.md +14 -0
  52. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  53. package/plugins/pbr/scripts/local-llm/metrics.js +121 -33
  54. package/plugins/pbr/skills/build/SKILL.md +12 -0
  55. package/plugins/pbr/skills/quick/SKILL.md +12 -0
  56. package/plugins/pbr/skills/review/SKILL.md +14 -0
@@ -1,16 +1,17 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
- <script>(function(){var t=localStorage.getItem('pbr-theme');if(t)document.documentElement.dataset.theme=t})()</script>
3
+ <script>(function(){var t=localStorage.getItem('pbr-theme');if(t)document.documentElement.dataset.bsTheme=t})()</script>
4
4
  <%- include('head', { title: typeof title !== 'undefined' ? title : 'PBR Dashboard' }) %>
5
- <body>
5
+ <body class="antialiased">
6
6
  <a href="#main-content" class="skip-link">Skip to main content</a>
7
7
  <div class="loading-bar"></div>
8
8
  <script>
9
9
  document.body.addEventListener('htmx:beforeRequest', function() { document.body.classList.add('htmx-request'); });
10
10
  document.body.addEventListener('htmx:afterRequest', function() { document.body.classList.remove('htmx-request'); });
11
11
  </script>
12
- <div class="page-wrapper">
12
+ <div class="wrapper">
13
13
  <%- include('header') %>
14
- <div class="sidebar-backdrop" onclick="closeSidebar()"></div>
15
14
  <%- include('sidebar', { activePage: typeof activePage !== 'undefined' ? activePage : '' }) %>
16
- <main id="main-content">
15
+ <div class="page-wrapper">
16
+ <div class="page-body">
17
+ <div class="container-xl" id="main-content">
@@ -5,28 +5,25 @@
5
5
  <%- include('empty-state', { icon: 'L', title: 'No log files found', action: 'Log files appear in .planning/logs/ when PBR hooks run with local LLM routing enabled.' }) %>
6
6
  <% } else { %>
7
7
 
8
- <div class="logs-layout" style="display:flex;gap:var(--spacing-md);align-items:flex-start">
8
+ <div class="d-flex gap-3 align-items-start">
9
9
 
10
10
  <%# File browser sidebar %>
11
11
  <aside style="min-width:220px;max-width:260px;flex-shrink:0">
12
- <article>
13
- <header><strong>Log Files</strong></header>
14
- <ul style="list-style:none;padding:0;margin:0">
12
+ <div class="card">
13
+ <div class="card-header"><strong>Log Files</strong></div>
14
+ <div class="list-group list-group-flush">
15
15
  <% logFiles.forEach(function(lf) { %>
16
- <li style="padding:0.25rem 0;border-bottom:1px solid var(--card-border)">
17
- <a href="/logs?file=<%= lf.name %>"
18
- hx-get="/logs?file=<%= lf.name %>"
19
- hx-target="#main-content"
20
- hx-push-url="true"
21
- <% if (selectedFile === lf.name) { %>aria-current="page"<% } %>>
22
- <%= lf.name %>
23
- </a>
24
- <br>
25
- <small><%= Math.round(lf.size / 1024) %> KB &middot; <%= lf.modified ? lf.modified.slice(0,10) : '' %></small>
26
- </li>
16
+ <a href="/logs?file=<%= lf.name %>"
17
+ hx-get="/logs?file=<%= lf.name %>"
18
+ hx-target="#main-content"
19
+ hx-push-url="true"
20
+ class="list-group-item list-group-item-action<% if (selectedFile === lf.name) { %> active<% } %>">
21
+ <%= lf.name %>
22
+ <br><small class="text-muted"><%= Math.round(lf.size / 1024) %> KB &middot; <%= lf.modified ? lf.modified.slice(0,10) : '' %></small>
23
+ </a>
27
24
  <% }); %>
28
- </ul>
29
- </article>
25
+ </div>
26
+ </div>
30
27
  </aside>
31
28
 
32
29
  <%# Main log viewer %>
@@ -37,27 +34,27 @@
37
34
  <form hx-get="/logs"
38
35
  hx-target="#main-content"
39
36
  hx-push-url="true"
40
- style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-bottom:var(--spacing-sm)">
37
+ class="d-flex flex-wrap gap-2 mb-2">
41
38
  <input type="hidden" name="file" value="<%= selectedFile %>">
42
39
  <input type="text" name="type" placeholder="Type filter (e.g. tool_use)"
43
40
  value="<%= typeof filters !== 'undefined' ? filters.type : '' %>"
44
- style="width:200px">
41
+ class="form-control" style="width:200px">
45
42
  <input type="search" name="q" placeholder="Search entries..."
46
43
  value="<%= typeof filters !== 'undefined' ? filters.q : '' %>"
47
- style="flex:1;min-width:160px">
48
- <button type="submit">Filter</button>
44
+ class="form-control flex-grow-1" style="min-width:160px">
45
+ <button type="submit" class="btn btn-outline-secondary">Filter</button>
49
46
  <% if (typeof filters !== 'undefined' && (filters.type || filters.q)) { %>
50
47
  <a href="/logs?file=<%= selectedFile %>"
51
48
  hx-get="/logs?file=<%= selectedFile %>"
52
49
  hx-target="#main-content"
53
50
  hx-push-url="true"
54
- role="button" class="secondary">Clear</a>
51
+ class="btn btn-outline-secondary">Clear</a>
55
52
  <% } %>
56
53
  </form>
57
54
 
58
55
  <%# Auto-scroll toggle + live indicator %>
59
- <div style="display:flex;align-items:center;gap:1rem;margin-bottom:var(--spacing-sm)">
60
- <label style="display:flex;align-items:center;gap:0.4rem;cursor:pointer">
56
+ <div class="d-flex align-items-center gap-3 mb-2">
57
+ <label class="d-flex align-items-center gap-2" style="cursor:pointer">
61
58
  <input type="checkbox" id="auto-scroll-toggle" checked style="margin:0">
62
59
  <small>Auto-scroll</small>
63
60
  </label>
@@ -77,14 +74,14 @@
77
74
 
78
75
  <%# Pagination %>
79
76
  <% if (logData && logData.total > logData.pageSize) { %>
80
- <nav style="display:flex;gap:0.5rem;margin-top:var(--spacing-sm);align-items:center">
77
+ <nav class="d-flex gap-2 mt-2 align-items-center">
81
78
  <% const totalPages = Math.ceil(logData.total / logData.pageSize); %>
82
79
  <% if (logData.page > 1) { %>
83
80
  <a href="/logs?file=<%= selectedFile %>&page=<%= logData.page - 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
84
81
  hx-get="/logs?file=<%= selectedFile %>&page=<%= logData.page - 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
85
82
  hx-target="#main-content"
86
83
  hx-push-url="true"
87
- role="button" class="secondary">&larr; Prev</a>
84
+ class="btn btn-outline-secondary">&larr; Prev</a>
88
85
  <% } %>
89
86
  <small>Page <%= logData.page %> of <%= totalPages %> (<%= logData.total %> entries)</small>
90
87
  <% if (logData.page < totalPages) { %>
@@ -92,14 +89,14 @@
92
89
  hx-get="/logs?file=<%= selectedFile %>&page=<%= logData.page + 1 %>&type=<%= (filters || {}).type || '' %>&q=<%= (filters || {}).q || '' %>"
93
90
  hx-target="#main-content"
94
91
  hx-push-url="true"
95
- role="button" class="secondary">Next &rarr;</a>
92
+ class="btn btn-outline-secondary">Next &rarr;</a>
96
93
  <% } %>
97
94
  </nav>
98
95
  <% } %>
99
96
 
100
97
  <% } else { %>
101
- <div style="text-align:center;padding:var(--space-xl) var(--space-md);color:var(--color-text-dim)">
102
- <p style="font-size:1.5rem;margin-bottom:var(--space-sm)">&#8592;</p>
98
+ <div class="text-center py-4 text-muted">
99
+ <p style="font-size:1.5rem;margin-bottom:0.5rem">&#8592;</p>
103
100
  <p>Select a log file from the list to view entries.</p>
104
101
  </div>
105
102
  <% } %>
@@ -9,12 +9,12 @@
9
9
  </p>
10
10
 
11
11
  <% sections.forEach(function(section) { %>
12
- <article>
13
- <header>
12
+ <div class="card mb-3">
13
+ <div class="card-header">
14
14
  <strong><%= section.type %></strong>
15
- </header>
16
- <div class="markdown-body">
15
+ </div>
16
+ <div class="card-body markdown-body">
17
17
  <%- section.html %>
18
18
  </div>
19
- </article>
19
+ </div>
20
20
  <% }); %>
@@ -25,40 +25,49 @@
25
25
  var totalPhases = msPhases.length;
26
26
  var completedPhases = msPhases.filter(function(p) { return p.status === 'complete'; }).length;
27
27
  %>
28
- <article>
29
- <header>
28
+ <div class="card mb-3">
29
+ <div class="card-header">
30
30
  <strong><%= m.name %></strong>
31
31
  <% if (m.goal) { %><small> &mdash; <%= m.goal %></small><% } %>
32
- </header>
33
- <p>
34
- Phases <%= m.startPhase %> &ndash; <%= m.endPhase %>
35
- </p>
36
- <% if (totalPhases > 0) { %>
37
- <progress value="<%= completedPhases %>" max="<%= totalPhases %>"></progress>
38
- <p><small><%= completedPhases %> of <%= totalPhases %> phases complete</small></p>
39
- <% } %>
40
- </article>
32
+ </div>
33
+ <div class="card-body">
34
+ <p>
35
+ Phases <%= m.startPhase %> &ndash; <%= m.endPhase %>
36
+ </p>
37
+ <% if (totalPhases > 0) { %>
38
+ <div class="progress mb-2" style="height:0.5rem">
39
+ <div class="progress-bar" role="progressbar"
40
+ style="width:<%= completedPhases / totalPhases * 100 %>%"
41
+ aria-valuenow="<%= completedPhases %>" aria-valuemin="0" aria-valuemax="<%= totalPhases %>">
42
+ </div>
43
+ </div>
44
+ <p><small><%= completedPhases %> of <%= totalPhases %> phases complete</small></p>
45
+ <% } %>
46
+ </div>
47
+ </div>
41
48
  <% }); %>
42
49
  <% } %>
43
50
 
44
51
  <% if (completedNotArchived.length > 0) { %>
45
52
  <h2>Completed (Pending Archive)</h2>
46
53
  <% completedNotArchived.forEach(function(m) { %>
47
- <article>
48
- <header>
54
+ <div class="card mb-3">
55
+ <div class="card-header">
49
56
  <strong><%= m.name %></strong>
50
57
  <span class="status-badge status-badge--sm" data-status="complete" style="float:right">completed</span>
51
- </header>
52
- <p>Phases <%= m.startPhase %> &ndash; <%= m.endPhase %></p>
53
- </article>
58
+ </div>
59
+ <div class="card-body">
60
+ <p>Phases <%= m.startPhase %> &ndash; <%= m.endPhase %></p>
61
+ </div>
62
+ </div>
54
63
  <% }); %>
55
64
  <% } %>
56
65
 
57
66
  <% if (archived.length > 0) { %>
58
67
  <h2>Archived</h2>
59
- <article>
60
- <div class="table-wrap">
61
- <table>
68
+ <div class="card mb-3">
69
+ <div class="table-responsive">
70
+ <table class="table table-vcenter">
62
71
  <thead>
63
72
  <tr>
64
73
  <th scope="col">Version</th>
@@ -75,17 +84,17 @@
75
84
  <td colspan="6" style="padding:0">
76
85
  <details>
77
86
  <summary style="padding:0.5rem 1rem;cursor:pointer">
78
- <span class="grid" style="display:inline-grid;grid-template-columns:60px 2fr 1fr 1fr 60px 60px;width:100%;text-align:left;gap:var(--space-sm)">
79
- <span><a href="/milestones/<%= m.version %>"
80
- hx-get="/milestones/<%= m.version %>"
81
- hx-target="#main-content"
82
- hx-push-url="true">v<%= m.version %></a></span>
83
- <span style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis"><%= m.name %></span>
84
- <span><%= m.date || '—' %></span>
85
- <span><%= m.duration || '—' %></span>
86
- <span><%= m.stats && m.stats.phaseCount ? m.stats.phaseCount : '—' %></span>
87
- <span><%= m.stats && m.stats.commitCount ? m.stats.commitCount : '—' %></span>
88
- </span>
87
+ <div class="row g-2" style="width:100%">
88
+ <div class="col-auto" style="min-width:60px"><a href="/milestones/<%= m.version %>"
89
+ hx-get="/milestones/<%= m.version %>"
90
+ hx-target="#main-content"
91
+ hx-push-url="true">v<%= m.version %></a></div>
92
+ <div class="col" style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis"><%= m.name %></div>
93
+ <div class="col-auto"><%= m.date || '—' %></div>
94
+ <div class="col-auto"><%= m.duration || '—' %></div>
95
+ <div class="col-auto"><%= m.stats && m.stats.phaseCount ? m.stats.phaseCount : '—' %></div>
96
+ <div class="col-auto"><%= m.stats && m.stats.commitCount ? m.stats.commitCount : '—' %></div>
97
+ </div>
89
98
  </summary>
90
99
  <div style="padding:0.5rem 1rem 1rem">
91
100
  <% if (m.stats && m.stats.statsHtml) { %>
@@ -110,7 +119,7 @@
110
119
  </tbody>
111
120
  </table>
112
121
  </div>
113
- </article>
122
+ </div>
114
123
  <% } %>
115
124
 
116
125
  <% if (active.length === 0 && archived.length === 0) { %>
@@ -10,13 +10,15 @@
10
10
  <p><small><%= date %></small></p>
11
11
  <% } %>
12
12
 
13
- <article>
14
- <div class="markdown-body">
15
- <%- html %>
13
+ <div class="card mb-3">
14
+ <div class="card-body">
15
+ <div class="markdown-body">
16
+ <%- html %>
17
+ </div>
16
18
  </div>
17
- </article>
19
+ </div>
18
20
 
19
- <p><a href="/notes"
21
+ <p><a href="/notes" class="btn btn-outline-secondary"
20
22
  hx-get="/notes"
21
23
  hx-target="#main-content"
22
24
  hx-push-url="true">Back to Notes</a></p>
@@ -6,8 +6,8 @@
6
6
  <% } else { %>
7
7
  <% notes.forEach(function(note) { %>
8
8
  <% const noteSlug = note.filename.replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, ''); %>
9
- <article class="card">
10
- <header class="card__header">
9
+ <div class="card mb-2">
10
+ <div class="card-header">
11
11
  <strong>
12
12
  <a href="/notes/<%= noteSlug %>"
13
13
  hx-get="/notes/<%= noteSlug %>"
@@ -22,7 +22,7 @@
22
22
  <small style="color:var(--color-text-dim)"><%= typeof note.date === 'object' ? note.date.toISOString().slice(0, 10) : String(note.date).slice(0, 10) %></small>
23
23
  <% } %>
24
24
  </span>
25
- </header>
26
- </article>
25
+ </div>
26
+ </div>
27
27
  <% }); %>
28
28
  <% } %>
@@ -39,13 +39,13 @@
39
39
  else if (vStatus === 'partial') verificationCssStatus = 'in-progress';
40
40
  %>
41
41
  <div class="card">
42
- <div class="card__header">
42
+ <div class="card-header">
43
43
  <strong>Verification Status</strong>
44
44
  <span style="float:right;">
45
45
  <span class="status-badge" data-status="<%= verificationCssStatus %>"><%= vStatus.toUpperCase() %></span>
46
46
  </span>
47
47
  </div>
48
- <div class="card__body">
48
+ <div class="card-body">
49
49
  <% if (verification.verified) { %>
50
50
  <p style="margin:0 0 var(--space-sm);">Verified: <%= new Date(verification.verified).toLocaleString() %></p>
51
51
  <% } %>
@@ -83,9 +83,7 @@
83
83
 
84
84
  <% if (plans.length === 0) { %>
85
85
  <!-- Empty State -->
86
- <article>
87
- <p>No plans found for this phase. This phase may not have been planned yet.</p>
88
- </article>
86
+ <%- include('empty-state', { title: 'No plans found for this phase.' }) %>
89
87
  <% } else { %>
90
88
 
91
89
  <h2>Plans (<%= plans.length %>)</h2>
@@ -103,7 +101,7 @@
103
101
  var planTitle = plan.planTitle || ('Plan ' + plan.planId);
104
102
  %>
105
103
  <div class="card">
106
- <div class="card__header">
104
+ <div class="card-header">
107
105
  <span><strong><%= planTitle %></strong></span>
108
106
  <span style="display:inline-flex;gap:var(--space-xs);align-items:center;float:right;">
109
107
  <% if (wave) { %>
@@ -115,7 +113,7 @@
115
113
  <span class="status-badge" data-status="<%= planCssStatus %>"><%= planStatus.replace(/-/g, ' ').toUpperCase() %></span>
116
114
  </span>
117
115
  </div>
118
- <div class="card__body">
116
+ <div class="card-body">
119
117
  <p style="margin:0 0 var(--space-sm);">
120
118
  <a href="/phases/<%= phaseId %>/<%= plan.planId %>/plan"
121
119
  hx-get="/phases/<%= phaseId %>/<%= plan.planId %>/plan"
@@ -203,7 +201,7 @@
203
201
  <h2>Commit History (<%= allCommits.length %>)</h2>
204
202
 
205
203
  <% if (allCommits.length === 0) { %>
206
- <div class="card"><div class="card__body"><p><em>No commits yet for this phase.</em></p></div></div>
204
+ <div class="card"><div class="card-body"><p><em>No commits yet for this phase.</em></p></div></div>
207
205
  <% } else { %>
208
206
  <ol class="commit-timeline">
209
207
  <% allCommits.forEach(function(entry) { %>
@@ -4,15 +4,13 @@
4
4
  <%- include('breadcrumbs', { breadcrumbs: typeof breadcrumbs !== 'undefined' ? breadcrumbs : [] }) %>
5
5
  <h1><%= docLabel %> <%= planId %></h1>
6
6
 
7
- <p><a href="/phases/<%= phaseId %>">&larr; Back to Phase <%= phaseId %>: <%= phaseName %></a></p>
7
+ <p><a href="/phases/<%= phaseId %>" class="btn btn-outline-secondary">&larr; Back to Phase <%= phaseId %>: <%= phaseName %></a></p>
8
8
 
9
9
  <% if (frontmatter && Object.keys(frontmatter).length > 0) { %>
10
- <article>
11
- <header>
12
- <strong>Metadata</strong>
13
- </header>
14
- <div class="table-wrap">
15
- <table>
10
+ <div class="card mb-3">
11
+ <div class="card-header"><strong>Metadata</strong></div>
12
+ <div class="table-responsive">
13
+ <table class="table table-vcenter">
16
14
  <tbody>
17
15
  <% Object.entries(frontmatter).forEach(function([key, val]) { %>
18
16
  <% if (typeof val !== 'object' || val === null) { %>
@@ -25,14 +23,14 @@
25
23
  </tbody>
26
24
  </table>
27
25
  </div>
28
- </article>
26
+ </div>
29
27
  <% } %>
30
28
 
31
- <article>
32
- <header>
33
- <strong>Content</strong>
34
- </header>
35
- <div class="markdown-body">
36
- <%- html %>
29
+ <div class="card mb-3">
30
+ <div class="card-header"><strong>Content</strong></div>
31
+ <div class="card-body">
32
+ <div class="markdown-body">
33
+ <%- html %>
34
+ </div>
37
35
  </div>
38
- </article>
36
+ </div>
@@ -1,20 +1,27 @@
1
1
  <% if (!phases || phases.length === 0) { %>
2
- <p class="muted">No phases found.</p>
2
+ <%- include('empty-state', { title: 'No phases found' }) %>
3
3
  <% } else { %>
4
- <ol class="phase-timeline">
4
+ <div class="list-group list-group-flush">
5
5
  <% phases.forEach(function(phase) { %>
6
- <li class="phase-step" data-status="<%= phase.status %>"<% if (currentPhase && phase.id === currentPhase.id) { %> aria-current="step"<% } %>>
7
- <span class="step-indicator"></span>
8
- <a class="step-label"
9
- href="/phases/<%= String(phase.id).padStart(2, '0') %>"
10
- hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
11
- hx-target="#main-content"
12
- hx-push-url="true">
13
- <strong>Phase <%= phase.id %></strong>
14
- <span class="step-name"><%= phase.name %></span>
15
- </a>
16
- <span class="status-badge status-badge--sm" data-status="<%= phase.status %>"><%= phase.status %></span>
17
- </li>
6
+ <div class="list-group-item<%= currentPhase && phase.id === currentPhase.id ? ' active' : '' %>"
7
+ data-status="<%= phase.status %>"
8
+ <% if (currentPhase && phase.id === currentPhase.id) { %> aria-current="step"<% } %>>
9
+ <div class="row align-items-center">
10
+ <div class="col">
11
+ <a class="text-decoration-none"
12
+ href="/phases/<%= String(phase.id).padStart(2, '0') %>"
13
+ hx-get="/phases/<%= String(phase.id).padStart(2, '0') %>"
14
+ hx-target="#main-content"
15
+ hx-push-url="true">
16
+ <strong class="me-2">Phase <%= phase.id %></strong>
17
+ <span class="text-muted small"><%= phase.name %></span>
18
+ </a>
19
+ </div>
20
+ <div class="col-auto">
21
+ <span class="status-badge status-badge--sm" data-status="<%= phase.status %>"><%= phase.status %></span>
22
+ </div>
23
+ </div>
24
+ </div>
18
25
  <% }); %>
19
- </ol>
26
+ </div>
20
27
  <% } %>