@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.
- package/CHANGELOG.md +14 -0
- package/README.md +29 -0
- package/dashboard/public/css/layout.css +7 -283
- package/dashboard/public/css/status-colors.css +7 -0
- package/dashboard/public/css/tokens.css +3 -3
- package/dashboard/public/js/sidebar-toggle.js +9 -31
- package/dashboard/public/js/theme-toggle.js +4 -4
- package/dashboard/src/views/partials/activity-feed.ejs +17 -9
- package/dashboard/src/views/partials/analytics-content.ejs +178 -88
- package/dashboard/src/views/partials/audit-detail-content.ejs +6 -4
- package/dashboard/src/views/partials/audits-content.ejs +28 -26
- package/dashboard/src/views/partials/breadcrumbs.ejs +8 -4
- package/dashboard/src/views/partials/config-content.ejs +98 -95
- package/dashboard/src/views/partials/dashboard-content.ejs +69 -60
- package/dashboard/src/views/partials/dependencies-content.ejs +5 -3
- package/dashboard/src/views/partials/empty-state.ejs +10 -5
- package/dashboard/src/views/partials/footer.ejs +8 -2
- package/dashboard/src/views/partials/head.ejs +2 -1
- package/dashboard/src/views/partials/header.ejs +16 -19
- package/dashboard/src/views/partials/layout-bottom.ejs +5 -40
- package/dashboard/src/views/partials/layout-top.ejs +6 -5
- package/dashboard/src/views/partials/logs-content.ejs +26 -29
- package/dashboard/src/views/partials/milestone-detail-content.ejs +5 -5
- package/dashboard/src/views/partials/milestones-content.ejs +40 -31
- package/dashboard/src/views/partials/note-detail-content.ejs +7 -5
- package/dashboard/src/views/partials/notes-content.ejs +4 -4
- package/dashboard/src/views/partials/phase-content.ejs +6 -8
- package/dashboard/src/views/partials/phase-doc-content.ejs +13 -15
- package/dashboard/src/views/partials/phase-timeline.ejs +22 -15
- package/dashboard/src/views/partials/phases-content.ejs +98 -84
- package/dashboard/src/views/partials/quick-content.ejs +34 -32
- package/dashboard/src/views/partials/quick-detail-content.ejs +20 -19
- package/dashboard/src/views/partials/requirements-content.ejs +6 -6
- package/dashboard/src/views/partials/research-content.ejs +14 -14
- package/dashboard/src/views/partials/research-detail-content.ejs +13 -11
- package/dashboard/src/views/partials/roadmap-content.ejs +145 -128
- package/dashboard/src/views/partials/sidebar.ejs +86 -140
- package/dashboard/src/views/partials/todo-create-content.ejs +51 -46
- package/dashboard/src/views/partials/todo-detail-content.ejs +26 -25
- package/dashboard/src/views/partials/todos-content.ejs +65 -62
- package/dashboard/src/views/partials/todos-done-content.ejs +33 -31
- package/package.json +1 -1
- package/plugins/copilot-pbr/plugin.json +1 -1
- package/plugins/copilot-pbr/skills/build/SKILL.md +12 -0
- package/plugins/copilot-pbr/skills/quick/SKILL.md +12 -0
- package/plugins/copilot-pbr/skills/review/SKILL.md +14 -0
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor-pbr/README.md +20 -0
- package/plugins/cursor-pbr/skills/build/SKILL.md +12 -0
- package/plugins/cursor-pbr/skills/quick/SKILL.md +12 -0
- package/plugins/cursor-pbr/skills/review/SKILL.md +14 -0
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/scripts/local-llm/metrics.js +121 -33
- package/plugins/pbr/skills/build/SKILL.md +12 -0
- package/plugins/pbr/skills/quick/SKILL.md +12 -0
- 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.
|
|
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="
|
|
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
|
-
<
|
|
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="
|
|
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
|
-
<
|
|
13
|
-
<header><strong>Log Files</strong></
|
|
14
|
-
<
|
|
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
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<br>
|
|
25
|
-
<small><%= Math.round(lf.size / 1024) %> KB · <%= 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 · <%= lf.modified ? lf.modified.slice(0,10) : '' %></small>
|
|
23
|
+
</a>
|
|
27
24
|
<% }); %>
|
|
28
|
-
</
|
|
29
|
-
</
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
+
class="btn btn-outline-secondary">Clear</a>
|
|
55
52
|
<% } %>
|
|
56
53
|
</form>
|
|
57
54
|
|
|
58
55
|
<%# Auto-scroll toggle + live indicator %>
|
|
59
|
-
<div
|
|
60
|
-
<label
|
|
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
|
|
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
|
-
|
|
84
|
+
class="btn btn-outline-secondary">← 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
|
-
|
|
92
|
+
class="btn btn-outline-secondary">Next →</a>
|
|
96
93
|
<% } %>
|
|
97
94
|
</nav>
|
|
98
95
|
<% } %>
|
|
99
96
|
|
|
100
97
|
<% } else { %>
|
|
101
|
-
<div
|
|
102
|
-
<p style="font-size:1.5rem;margin-bottom:
|
|
98
|
+
<div class="text-center py-4 text-muted">
|
|
99
|
+
<p style="font-size:1.5rem;margin-bottom:0.5rem">←</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
|
-
<
|
|
13
|
-
<header>
|
|
12
|
+
<div class="card mb-3">
|
|
13
|
+
<div class="card-header">
|
|
14
14
|
<strong><%= section.type %></strong>
|
|
15
|
-
</
|
|
16
|
-
<div class="markdown-body">
|
|
15
|
+
</div>
|
|
16
|
+
<div class="card-body markdown-body">
|
|
17
17
|
<%- section.html %>
|
|
18
18
|
</div>
|
|
19
|
-
</
|
|
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
|
-
<
|
|
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> — <%= m.goal %></small><% } %>
|
|
32
|
-
</
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
</div>
|
|
33
|
+
<div class="card-body">
|
|
34
|
+
<p>
|
|
35
|
+
Phases <%= m.startPhase %> – <%= 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
|
-
<
|
|
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
|
-
</
|
|
52
|
-
<
|
|
53
|
-
|
|
58
|
+
</div>
|
|
59
|
+
<div class="card-body">
|
|
60
|
+
<p>Phases <%= m.startPhase %> – <%= 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
|
-
<
|
|
60
|
-
<div class="table-
|
|
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
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
</
|
|
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
|
-
</
|
|
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
|
-
<
|
|
14
|
-
<div class="
|
|
15
|
-
|
|
13
|
+
<div class="card mb-3">
|
|
14
|
+
<div class="card-body">
|
|
15
|
+
<div class="markdown-body">
|
|
16
|
+
<%- html %>
|
|
17
|
+
</div>
|
|
16
18
|
</div>
|
|
17
|
-
</
|
|
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
|
-
<
|
|
10
|
-
<
|
|
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
|
-
</
|
|
26
|
-
</
|
|
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="
|
|
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="
|
|
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
|
-
|
|
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="
|
|
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="
|
|
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="
|
|
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 %>">← Back to Phase <%= phaseId %>: <%= phaseName %></a></p>
|
|
7
|
+
<p><a href="/phases/<%= phaseId %>" class="btn btn-outline-secondary">← Back to Phase <%= phaseId %>: <%= phaseName %></a></p>
|
|
8
8
|
|
|
9
9
|
<% if (frontmatter && Object.keys(frontmatter).length > 0) { %>
|
|
10
|
-
<
|
|
11
|
-
<header>
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
</
|
|
26
|
+
</div>
|
|
29
27
|
<% } %>
|
|
30
28
|
|
|
31
|
-
<
|
|
32
|
-
<header>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
</
|
|
36
|
+
</div>
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
<% if (!phases || phases.length === 0) { %>
|
|
2
|
-
|
|
2
|
+
<%- include('empty-state', { title: 'No phases found' }) %>
|
|
3
3
|
<% } else { %>
|
|
4
|
-
<
|
|
4
|
+
<div class="list-group list-group-flush">
|
|
5
5
|
<% phases.forEach(function(phase) { %>
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
</
|
|
26
|
+
</div>
|
|
20
27
|
<% } %>
|