@beastmode-develeap/beastmode 0.1.204 → 0.1.205
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/dist/web/board.html +6 -190
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +1 -1
package/dist/web/board.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
17
|
<!--BOARD_DATA-->
|
|
18
|
-
<script>window.__BUILD_STAMP__ = "20260509-
|
|
18
|
+
<script>window.__BUILD_STAMP__ = "20260509-102837-ac9ac7b";</script>
|
|
19
19
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
20
20
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
21
21
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -1522,37 +1522,6 @@ input[type="range"]::-webkit-slider-thumb {
|
|
|
1522
1522
|
margin: 0 -8px;
|
|
1523
1523
|
padding: 8px;
|
|
1524
1524
|
}
|
|
1525
|
-
/* Audit Trail — Superseded transition history (Story 6) */
|
|
1526
|
-
.audit-timeline { display: flex; flex-direction: column; gap: 8px; }
|
|
1527
|
-
.audit-entry {
|
|
1528
|
-
background: var(--bg-input);
|
|
1529
|
-
border-radius: 8px;
|
|
1530
|
-
padding: 10px 12px;
|
|
1531
|
-
border-left: 3px solid var(--accent);
|
|
1532
|
-
}
|
|
1533
|
-
.audit-entry-header {
|
|
1534
|
-
display: flex;
|
|
1535
|
-
justify-content: space-between;
|
|
1536
|
-
align-items: center;
|
|
1537
|
-
margin-bottom: 4px;
|
|
1538
|
-
}
|
|
1539
|
-
.audit-actor { font-weight: 600; font-size: 13px; }
|
|
1540
|
-
.audit-time { color: var(--text-muted); font-size: 12px; }
|
|
1541
|
-
.audit-entry-body { font-size: 13px; display: flex; flex-direction: column; gap: 4px; }
|
|
1542
|
-
.audit-from { color: var(--text-muted); }
|
|
1543
|
-
.audit-successor a, .audit-cascade a {
|
|
1544
|
-
color: var(--accent);
|
|
1545
|
-
text-decoration: none;
|
|
1546
|
-
}
|
|
1547
|
-
.audit-successor a:hover, .audit-cascade a:hover { text-decoration: underline; }
|
|
1548
|
-
.audit-reason {
|
|
1549
|
-
margin-top: 4px;
|
|
1550
|
-
padding: 6px 8px;
|
|
1551
|
-
background: var(--bg-secondary);
|
|
1552
|
-
border-radius: 4px;
|
|
1553
|
-
font-style: italic;
|
|
1554
|
-
color: var(--text-secondary);
|
|
1555
|
-
}
|
|
1556
1525
|
.btn-env-deploy {
|
|
1557
1526
|
display: inline-flex;
|
|
1558
1527
|
align-items: center;
|
|
@@ -4291,7 +4260,7 @@ const STATUSES = [
|
|
|
4291
4260
|
'Waiting for Infra Approval', 'Provisioning Infra', 'Infra Ready',
|
|
4292
4261
|
'Approved & Merge to Main', 'Ready For Review',
|
|
4293
4262
|
'Verifying Prod with Tests', 'Verifying Epic', 'Waiting for Epic Bugs', 'Awaiting Input',
|
|
4294
|
-
'Epic Breakdown Posted', 'Stuck', 'Done',
|
|
4263
|
+
'Epic Breakdown Posted', 'Stuck', 'Done',
|
|
4295
4264
|
];
|
|
4296
4265
|
|
|
4297
4266
|
const KANBAN_COLUMNS = [
|
|
@@ -5126,88 +5095,12 @@ function DeployModal({ envName, refs, loading, item, selectedProject, onClose })
|
|
|
5126
5095
|
`;
|
|
5127
5096
|
}
|
|
5128
5097
|
|
|
5129
|
-
// ── Superseded Modal ──
|
|
5130
|
-
|
|
5131
|
-
function SupersededModal({ item, onConfirm, onCancel }) {
|
|
5132
|
-
const [supersededBy, setSupersededBy] = useState('');
|
|
5133
|
-
const [reason, setReason] = useState('');
|
|
5134
|
-
const [error, setError] = useState('');
|
|
5135
|
-
const [loading, setLoading] = useState(false);
|
|
5136
|
-
|
|
5137
|
-
const isEpic = (item.task_type || '').toLowerCase() === 'epic';
|
|
5138
|
-
|
|
5139
|
-
useEffect(() => {
|
|
5140
|
-
const handleEsc = (e) => { if (e.key === 'Escape') onCancel(); };
|
|
5141
|
-
document.addEventListener('keydown', handleEsc);
|
|
5142
|
-
return () => document.removeEventListener('keydown', handleEsc);
|
|
5143
|
-
}, []);
|
|
5144
|
-
|
|
5145
|
-
const handleSubmit = async () => {
|
|
5146
|
-
if (isEpic && !supersededBy.trim()) {
|
|
5147
|
-
setError('Epics require a successor item ID (superseded_by).');
|
|
5148
|
-
return;
|
|
5149
|
-
}
|
|
5150
|
-
if (isEpic && !reason.trim()) {
|
|
5151
|
-
setError('Epics require a supersede reason.');
|
|
5152
|
-
return;
|
|
5153
|
-
}
|
|
5154
|
-
setLoading(true);
|
|
5155
|
-
setError('');
|
|
5156
|
-
try {
|
|
5157
|
-
const payload = { status: 'Superseded' };
|
|
5158
|
-
const extra = {};
|
|
5159
|
-
if (supersededBy.trim()) extra.superseded_by = supersededBy.trim();
|
|
5160
|
-
if (reason.trim()) extra.superseded_reason = reason.trim();
|
|
5161
|
-
if (Object.keys(extra).length > 0) payload.extra_columns = extra;
|
|
5162
|
-
await api('PATCH', '/api/board/items/' + item.id, payload);
|
|
5163
|
-
onConfirm();
|
|
5164
|
-
} catch (err) {
|
|
5165
|
-
setError(err.message || 'Failed to update status');
|
|
5166
|
-
} finally {
|
|
5167
|
-
setLoading(false);
|
|
5168
|
-
}
|
|
5169
|
-
};
|
|
5170
|
-
|
|
5171
|
-
return html`
|
|
5172
|
-
<div class="deploy-modal-overlay" data-testid="superseded-modal-overlay"
|
|
5173
|
-
onClick=${(e) => { if (e.target === e.currentTarget) onCancel(); }}>
|
|
5174
|
-
<div class="deploy-modal" data-testid="superseded-modal">
|
|
5175
|
-
<h3>Mark as Superseded</h3>
|
|
5176
|
-
<p style="font-size:13px;color:var(--text-muted);margin:0 0 12px;">
|
|
5177
|
-
This action is permanent. Superseded items cannot be reactivated.
|
|
5178
|
-
</p>
|
|
5179
|
-
${error && html`<div class="error-banner" style="margin-bottom:12px;">${error}</div>`}
|
|
5180
|
-
<label>Successor item ID ${isEpic ? html`<span style="color:var(--danger);">*</span>` : '(optional)'}</label>
|
|
5181
|
-
<input type="text" value=${supersededBy}
|
|
5182
|
-
data-testid="superseded-by-input"
|
|
5183
|
-
placeholder="e.g. 42"
|
|
5184
|
-
onInput=${(e) => setSupersededBy(e.target.value)}
|
|
5185
|
-
style="width:100%;padding:8px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;box-sizing:border-box;" />
|
|
5186
|
-
<label>Reason ${isEpic ? html`<span style="color:var(--danger);">*</span>` : '(optional)'}</label>
|
|
5187
|
-
<textarea value=${reason}
|
|
5188
|
-
data-testid="superseded-reason-input"
|
|
5189
|
-
placeholder="Why is this item being superseded?"
|
|
5190
|
-
onInput=${(e) => setReason(e.target.value)}
|
|
5191
|
-
style="width:100%;padding:8px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;box-sizing:border-box;resize:vertical;min-height:60px;" />
|
|
5192
|
-
<div class="deploy-modal-actions">
|
|
5193
|
-
<button onClick=${onCancel} disabled=${loading}>Cancel</button>
|
|
5194
|
-
<button class="btn-primary" onClick=${handleSubmit} disabled=${loading}
|
|
5195
|
-
data-testid="superseded-confirm-btn">
|
|
5196
|
-
${loading ? 'Updating...' : 'Mark Superseded'}
|
|
5197
|
-
</button>
|
|
5198
|
-
</div>
|
|
5199
|
-
</div>
|
|
5200
|
-
</div>
|
|
5201
|
-
`;
|
|
5202
|
-
}
|
|
5203
|
-
|
|
5204
5098
|
// ── Item Detail Sidebar ──
|
|
5205
5099
|
|
|
5206
|
-
function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject
|
|
5100
|
+
function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject }) {
|
|
5207
5101
|
const [updates, setUpdates] = useState([]);
|
|
5208
5102
|
const [loadingUpdates, setLoadingUpdates] = useState(true);
|
|
5209
5103
|
const [sortNewest, setSortNewest] = useState(true);
|
|
5210
|
-
const [showSupersededModal, setShowSupersededModal] = useState(false);
|
|
5211
5104
|
// Attachments (Gap 8a — 2026-04-15): list every attachment the
|
|
5212
5105
|
// board service has for this item, render image types as thumbnails
|
|
5213
5106
|
// that open in a new tab when clicked.
|
|
@@ -5218,7 +5111,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
|
|
|
5218
5111
|
const [phaseData, setPhaseData] = useState([]);
|
|
5219
5112
|
const [envTimeline, setEnvTimeline] = useState(null);
|
|
5220
5113
|
const [deployModal, setDeployModal] = useState(null);
|
|
5221
|
-
const [auditEvents, setAuditEvents] = useState([]);
|
|
5222
5114
|
const sidebarRef = useRef(null);
|
|
5223
5115
|
const topCommentRef = useRef(null);
|
|
5224
5116
|
|
|
@@ -5240,25 +5132,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
|
|
|
5240
5132
|
.catch(() => setEnvTimeline(null));
|
|
5241
5133
|
}, [item && item.id, item && item.extra && item.extra.current_env]);
|
|
5242
5134
|
|
|
5243
|
-
// FR-4: Fetch SupersededTransition audit events for the selected item.
|
|
5244
|
-
// Only fires when the item is in Superseded status — non-Superseded items
|
|
5245
|
-
// never have audit entries and the section is hidden anyway (NFR-4).
|
|
5246
|
-
useEffect(() => {
|
|
5247
|
-
if (!item || item.status !== 'Superseded') {
|
|
5248
|
-
setAuditEvents([]);
|
|
5249
|
-
return;
|
|
5250
|
-
}
|
|
5251
|
-
const proj = selectedProject && selectedProject !== 'all' ? selectedProject : null;
|
|
5252
|
-
const qs = proj ? '?board=' + encodeURIComponent(proj) : '';
|
|
5253
|
-
const sep = qs ? '&' : '?';
|
|
5254
|
-
const url = '/api/events' + qs + sep + 'item_id=' + encodeURIComponent(item.id) +
|
|
5255
|
-
'&type=SupersededTransition';
|
|
5256
|
-
fetch(url)
|
|
5257
|
-
.then(r => r.ok ? r.json() : [])
|
|
5258
|
-
.then(events => setAuditEvents(Array.isArray(events) ? events : []))
|
|
5259
|
-
.catch(() => setAuditEvents([]));
|
|
5260
|
-
}, [item && item.id, item && item.status]);
|
|
5261
|
-
|
|
5262
5135
|
// Cost summary fetch — keyed on item.id, refreshed alongside the
|
|
5263
5136
|
// 10-second updates/attachments poll below. The api() helper only
|
|
5264
5137
|
// auto-scopes /api/board/*, so append ?board=<proj> manually.
|
|
@@ -5427,17 +5300,9 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
|
|
|
5427
5300
|
</div>
|
|
5428
5301
|
<div class="detail-fields">
|
|
5429
5302
|
<label>Status</label>
|
|
5430
|
-
<select value=${item.status || 'New'}
|
|
5431
|
-
disabled=${item.status === 'Superseded'}
|
|
5432
|
-
onChange=${async (e) => {
|
|
5433
|
-
const newStatus = e.target.value;
|
|
5434
|
-
if (newStatus === 'Superseded') {
|
|
5435
|
-
e.target.value = item.status || 'New';
|
|
5436
|
-
setShowSupersededModal(true);
|
|
5437
|
-
return;
|
|
5438
|
-
}
|
|
5303
|
+
<select value=${item.status || 'New'} onChange=${async (e) => {
|
|
5439
5304
|
try {
|
|
5440
|
-
await api('PATCH', '/api/board/items/' + item.id, { status:
|
|
5305
|
+
await api('PATCH', '/api/board/items/' + item.id, { status: e.target.value });
|
|
5441
5306
|
onStatusChange();
|
|
5442
5307
|
} catch (err) { /* ignore */ }
|
|
5443
5308
|
}}>
|
|
@@ -5586,49 +5451,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
|
|
|
5586
5451
|
</div>
|
|
5587
5452
|
</div>
|
|
5588
5453
|
`}
|
|
5589
|
-
${item && item.status === 'Superseded' && auditEvents.length > 0 && html`
|
|
5590
|
-
<div class="detail-section" style="padding:12px 24px 0;" data-testid="audit-trail-section">
|
|
5591
|
-
<h4 class="detail-section-title" style="margin:0 0 8px 0;font-size:13px;font-weight:600;">Audit Trail</h4>
|
|
5592
|
-
<div class="audit-timeline">
|
|
5593
|
-
${auditEvents.map(ev => {
|
|
5594
|
-
const payload = (ev && ev.payload) || {};
|
|
5595
|
-
const isCascade = ev.actor === 'cascade';
|
|
5596
|
-
return html`
|
|
5597
|
-
<div class="audit-entry" key=${ev.id} data-testid="audit-entry">
|
|
5598
|
-
<div class="audit-entry-header">
|
|
5599
|
-
<span class="audit-actor" data-testid="audit-actor">${isCascade ? 'Auto-cascaded' : 'Status changed'}</span>
|
|
5600
|
-
<span class="audit-time" data-testid="audit-time">${timeAgo(ev.timestamp)}</span>
|
|
5601
|
-
</div>
|
|
5602
|
-
<div class="audit-entry-body">
|
|
5603
|
-
<span class="audit-from" data-testid="audit-from-status">From: <strong>${payload.from_status || ''}</strong></span>
|
|
5604
|
-
${payload.superseded_by && html`
|
|
5605
|
-
<span class="audit-successor">
|
|
5606
|
-
Successor: <a href="#" data-testid="audit-successor-link" onClick=${(e) => {
|
|
5607
|
-
e.preventDefault();
|
|
5608
|
-
const target = allItems && allItems.find ? allItems.find(i => String(i.id) === String(payload.superseded_by)) : null;
|
|
5609
|
-
if (target && onSelectItem) onSelectItem(target);
|
|
5610
|
-
}}>#${payload.superseded_by}</a>
|
|
5611
|
-
</span>
|
|
5612
|
-
`}
|
|
5613
|
-
${payload.cascade_source && html`
|
|
5614
|
-
<span class="audit-cascade">
|
|
5615
|
-
Cascaded from parent: <a href="#" data-testid="audit-cascade-source" onClick=${(e) => {
|
|
5616
|
-
e.preventDefault();
|
|
5617
|
-
const target = allItems && allItems.find ? allItems.find(i => String(i.id) === String(payload.cascade_source)) : null;
|
|
5618
|
-
if (target && onSelectItem) onSelectItem(target);
|
|
5619
|
-
}}>#${payload.cascade_source}</a>
|
|
5620
|
-
</span>
|
|
5621
|
-
`}
|
|
5622
|
-
${payload.superseded_reason && html`
|
|
5623
|
-
<div class="audit-reason" data-testid="audit-reason">${payload.superseded_reason}</div>
|
|
5624
|
-
`}
|
|
5625
|
-
</div>
|
|
5626
|
-
</div>
|
|
5627
|
-
`;
|
|
5628
|
-
})}
|
|
5629
|
-
</div>
|
|
5630
|
-
</div>
|
|
5631
|
-
`}
|
|
5632
5454
|
${(loadingAttachments || attachments.length > 0) && html`
|
|
5633
5455
|
<div style="padding:12px 24px 0;">
|
|
5634
5456
|
<h4 style="margin:0 0 8px 0;font-size:13px;font-weight:600;">
|
|
@@ -5685,11 +5507,6 @@ function ItemDetailSidebar({ item, onClose, onStatusChange, selectedProject, all
|
|
|
5685
5507
|
selectedProject=${selectedProject}
|
|
5686
5508
|
onClose=${() => setDeployModal(null)}
|
|
5687
5509
|
/>`}
|
|
5688
|
-
${showSupersededModal && html`<${SupersededModal}
|
|
5689
|
-
item=${item}
|
|
5690
|
-
onConfirm=${() => { setShowSupersededModal(false); onStatusChange(); }}
|
|
5691
|
-
onCancel=${() => setShowSupersededModal(false)}
|
|
5692
|
-
/>`}
|
|
5693
5510
|
`;
|
|
5694
5511
|
}
|
|
5695
5512
|
|
|
@@ -6542,7 +6359,6 @@ function BoardPage({ selectedProject }) {
|
|
|
6542
6359
|
e.preventDefault();
|
|
6543
6360
|
e.currentTarget.classList.remove('drag-over', 'drag-over-invalid');
|
|
6544
6361
|
e.currentTarget.removeAttribute('title');
|
|
6545
|
-
if (status === 'Superseded') return;
|
|
6546
6362
|
const di = dragInfoRef.current;
|
|
6547
6363
|
if (!di) return;
|
|
6548
6364
|
const validStages = (typeof getStagesForType === 'function')
|
|
@@ -7025,7 +6841,7 @@ function BoardPage({ selectedProject }) {
|
|
|
7025
6841
|
`}
|
|
7026
6842
|
|
|
7027
6843
|
${showCreateDialog && html`<${CreateTaskDialog} onClose=${() => setShowCreateDialog(false)} onCreated=${fetchItems} />`}
|
|
7028
|
-
${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject}
|
|
6844
|
+
${selectedItem && html`<${ItemDetailSidebar} item=${selectedItem} selectedProject=${selectedProject} onClose=${() => setSelectedItem(null)} onStatusChange=${() => { fetchItems(); setSelectedItem(null); }} />`}
|
|
7029
6845
|
</div>
|
|
7030
6846
|
`;
|
|
7031
6847
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ac9ac7bd53cf29439c9062ac8b847daa409df1ca
|
package/dist/web/build-stamp.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
20260509-
|
|
1
|
+
20260509-102837-ac9ac7b
|