hzl-web 1.19.1 → 1.20.0
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/ui/index.html +203 -28
- package/package.json +2 -2
package/dist/ui/index.html
CHANGED
|
@@ -531,6 +531,18 @@
|
|
|
531
531
|
font-size: 12px;
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
+
.modal-progress {
|
|
535
|
+
color: var(--accent);
|
|
536
|
+
background: rgba(245, 158, 11, 0.15);
|
|
537
|
+
padding: 2px 8px;
|
|
538
|
+
border-radius: 4px;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.modal-progress.complete {
|
|
542
|
+
color: var(--status-done);
|
|
543
|
+
background: rgba(34, 197, 94, 0.15);
|
|
544
|
+
}
|
|
545
|
+
|
|
534
546
|
.modal-description {
|
|
535
547
|
background: var(--bg-primary);
|
|
536
548
|
padding: 12px;
|
|
@@ -571,6 +583,73 @@
|
|
|
571
583
|
white-space: pre-wrap;
|
|
572
584
|
}
|
|
573
585
|
|
|
586
|
+
.show-more-btn {
|
|
587
|
+
width: 100%;
|
|
588
|
+
padding: 8px;
|
|
589
|
+
background: var(--bg-primary);
|
|
590
|
+
border: 1px dashed var(--border);
|
|
591
|
+
border-radius: 4px;
|
|
592
|
+
color: var(--text-secondary);
|
|
593
|
+
font-family: var(--font-mono);
|
|
594
|
+
font-size: 12px;
|
|
595
|
+
cursor: pointer;
|
|
596
|
+
transition: border-color 0.15s, color 0.15s;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.show-more-btn:hover {
|
|
600
|
+
border-color: var(--accent);
|
|
601
|
+
color: var(--accent);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.modal-tabs {
|
|
605
|
+
display: flex;
|
|
606
|
+
gap: 0;
|
|
607
|
+
border-bottom: 1px solid var(--border);
|
|
608
|
+
margin-bottom: 12px;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.modal-tab {
|
|
612
|
+
padding: 8px 16px;
|
|
613
|
+
background: none;
|
|
614
|
+
border: none;
|
|
615
|
+
color: var(--text-muted);
|
|
616
|
+
font-family: var(--font-mono);
|
|
617
|
+
font-size: 12px;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
border-bottom: 2px solid transparent;
|
|
620
|
+
margin-bottom: -1px;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.modal-tab:hover {
|
|
624
|
+
color: var(--text-primary);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.modal-tab.active {
|
|
628
|
+
color: var(--accent);
|
|
629
|
+
border-bottom-color: var(--accent);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.modal-tab:disabled {
|
|
633
|
+
opacity: 0.4;
|
|
634
|
+
cursor: not-allowed;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.modal-tab-count {
|
|
638
|
+
background: var(--bg-primary);
|
|
639
|
+
padding: 1px 6px;
|
|
640
|
+
border-radius: 8px;
|
|
641
|
+
font-size: 10px;
|
|
642
|
+
margin-left: 6px;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
.modal-tab-content {
|
|
646
|
+
display: none;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.modal-tab-content.active {
|
|
650
|
+
display: block;
|
|
651
|
+
}
|
|
652
|
+
|
|
574
653
|
/* Activity Panel */
|
|
575
654
|
.activity-panel {
|
|
576
655
|
position: fixed;
|
|
@@ -1003,6 +1082,11 @@
|
|
|
1003
1082
|
let pollTimer = null;
|
|
1004
1083
|
let lastPollTime = null;
|
|
1005
1084
|
let selectedTask = null;
|
|
1085
|
+
let showAllComments = false;
|
|
1086
|
+
let showAllCheckpoints = false;
|
|
1087
|
+
let activeModalTab = 'comments';
|
|
1088
|
+
const COMMENT_DISPLAY_LIMIT = 15;
|
|
1089
|
+
const CHECKPOINT_DISPLAY_LIMIT = 15;
|
|
1006
1090
|
let activeTab = 'ready';
|
|
1007
1091
|
let activeView = 'kanban';
|
|
1008
1092
|
let graphInstance = null;
|
|
@@ -1645,10 +1729,17 @@
|
|
|
1645
1729
|
}).join('');
|
|
1646
1730
|
}
|
|
1647
1731
|
|
|
1648
|
-
async function openTaskModal(taskId) {
|
|
1732
|
+
async function openTaskModal(taskId, preserveShowAll = false) {
|
|
1649
1733
|
// Track this request to handle rapid clicks (race condition prevention)
|
|
1650
1734
|
const requestId = ++pendingModalRequestId;
|
|
1651
1735
|
|
|
1736
|
+
// Reset expansion state for new tasks
|
|
1737
|
+
if (!preserveShowAll) {
|
|
1738
|
+
showAllComments = false;
|
|
1739
|
+
showAllCheckpoints = false;
|
|
1740
|
+
activeModalTab = 'comments';
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1652
1743
|
try {
|
|
1653
1744
|
const data = await fetchTaskDetail(taskId);
|
|
1654
1745
|
|
|
@@ -1658,6 +1749,9 @@
|
|
|
1658
1749
|
selectedTask = data;
|
|
1659
1750
|
modalTitle.textContent = data.task.title;
|
|
1660
1751
|
|
|
1752
|
+
const progressValue = data.task.progress ?? 0;
|
|
1753
|
+
const progressClass = progressValue >= 100 ? 'modal-progress complete' : 'modal-progress';
|
|
1754
|
+
|
|
1661
1755
|
let html = `
|
|
1662
1756
|
<div class="modal-section">
|
|
1663
1757
|
<div class="modal-meta">
|
|
@@ -1665,6 +1759,10 @@
|
|
|
1665
1759
|
<div class="modal-meta-label">Status</div>
|
|
1666
1760
|
<div class="modal-meta-value">${data.task.status}</div>
|
|
1667
1761
|
</div>
|
|
1762
|
+
<div class="modal-meta-item">
|
|
1763
|
+
<div class="modal-meta-label">Progress</div>
|
|
1764
|
+
<div class="modal-meta-value"><span class="${progressClass}">${progressValue}%</span></div>
|
|
1765
|
+
</div>
|
|
1668
1766
|
<div class="modal-meta-item">
|
|
1669
1767
|
<div class="modal-meta-label">Project</div>
|
|
1670
1768
|
<div class="modal-meta-value">${escapeHtml(data.task.project)}</div>
|
|
@@ -1719,39 +1817,79 @@
|
|
|
1719
1817
|
`;
|
|
1720
1818
|
}
|
|
1721
1819
|
|
|
1722
|
-
|
|
1820
|
+
// Tabbed interface for comments and checkpoints
|
|
1821
|
+
if (data.comments.length > 0 || data.checkpoints.length > 0) {
|
|
1822
|
+
// Default to checkpoints tab if no comments
|
|
1823
|
+
if (data.comments.length === 0 && activeModalTab === 'comments') {
|
|
1824
|
+
activeModalTab = 'checkpoints';
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
const hasMoreComments = data.comments.length > COMMENT_DISPLAY_LIMIT && !showAllComments;
|
|
1828
|
+
const visibleComments = hasMoreComments
|
|
1829
|
+
? data.comments.slice(-COMMENT_DISPLAY_LIMIT)
|
|
1830
|
+
: data.comments;
|
|
1831
|
+
const hiddenCommentCount = Math.max(0, data.comments.length - COMMENT_DISPLAY_LIMIT);
|
|
1832
|
+
|
|
1833
|
+
const hasMoreCheckpoints = data.checkpoints.length > CHECKPOINT_DISPLAY_LIMIT && !showAllCheckpoints;
|
|
1834
|
+
const visibleCheckpoints = hasMoreCheckpoints
|
|
1835
|
+
? data.checkpoints.slice(-CHECKPOINT_DISPLAY_LIMIT)
|
|
1836
|
+
: data.checkpoints;
|
|
1837
|
+
const hiddenCheckpointCount = Math.max(0, data.checkpoints.length - CHECKPOINT_DISPLAY_LIMIT);
|
|
1838
|
+
|
|
1723
1839
|
html += `
|
|
1724
1840
|
<div class="modal-section">
|
|
1725
|
-
<div class="modal-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1841
|
+
<div class="modal-tabs">
|
|
1842
|
+
<button class="modal-tab ${activeModalTab === 'comments' ? 'active' : ''}" data-tab="comments" ${data.comments.length === 0 ? 'disabled' : ''}>
|
|
1843
|
+
Comments<span class="modal-tab-count">${data.comments.length}</span>
|
|
1844
|
+
</button>
|
|
1845
|
+
<button class="modal-tab ${activeModalTab === 'checkpoints' ? 'active' : ''}" data-tab="checkpoints" ${data.checkpoints.length === 0 ? 'disabled' : ''}>
|
|
1846
|
+
Checkpoints<span class="modal-tab-count">${data.checkpoints.length}</span>
|
|
1847
|
+
</button>
|
|
1848
|
+
</div>
|
|
1849
|
+
|
|
1850
|
+
<div class="modal-tab-content ${activeModalTab === 'comments' ? 'active' : ''}" data-tab-content="comments">
|
|
1851
|
+
${data.comments.length === 0 ? '<div class="empty-column">No comments</div>' : `
|
|
1852
|
+
<div class="modal-comments">
|
|
1853
|
+
${hasMoreComments ? `
|
|
1854
|
+
<button class="show-more-btn" id="showMoreComments">
|
|
1855
|
+
Show ${hiddenCommentCount} earlier comment${hiddenCommentCount === 1 ? '' : 's'}
|
|
1856
|
+
</button>
|
|
1857
|
+
` : ''}
|
|
1858
|
+
${visibleComments.map(c => `
|
|
1859
|
+
<div class="comment">
|
|
1860
|
+
<div class="comment-header">
|
|
1861
|
+
<span class="comment-author">${escapeHtml(c.agent_id || c.author || 'Unknown')}</span>
|
|
1862
|
+
<span>${formatTime(c.timestamp)}</span>
|
|
1863
|
+
</div>
|
|
1864
|
+
<div class="comment-text">${escapeHtml(c.text)}</div>
|
|
1865
|
+
</div>
|
|
1866
|
+
`).join('')}
|
|
1734
1867
|
</div>
|
|
1735
|
-
`
|
|
1868
|
+
`}
|
|
1736
1869
|
</div>
|
|
1737
|
-
</div>
|
|
1738
|
-
`;
|
|
1739
|
-
}
|
|
1740
1870
|
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1871
|
+
<div class="modal-tab-content ${activeModalTab === 'checkpoints' ? 'active' : ''}" data-tab-content="checkpoints">
|
|
1872
|
+
${data.checkpoints.length === 0 ? '<div class="empty-column">No checkpoints</div>' : `
|
|
1873
|
+
<div class="modal-comments">
|
|
1874
|
+
${hasMoreCheckpoints ? `
|
|
1875
|
+
<button class="show-more-btn" id="showMoreCheckpoints">
|
|
1876
|
+
Show ${hiddenCheckpointCount} earlier checkpoint${hiddenCheckpointCount === 1 ? '' : 's'}
|
|
1877
|
+
</button>
|
|
1878
|
+
` : ''}
|
|
1879
|
+
${visibleCheckpoints.map(cp => {
|
|
1880
|
+
const hasData = cp.data && Object.keys(cp.data).length > 0;
|
|
1881
|
+
return `
|
|
1882
|
+
<div class="comment">
|
|
1883
|
+
<div class="comment-header">
|
|
1884
|
+
<span class="comment-author">${escapeHtml(cp.name)}</span>
|
|
1885
|
+
<span>${formatTime(cp.timestamp)}</span>
|
|
1886
|
+
</div>
|
|
1887
|
+
${hasData ? `<div class="comment-text">${escapeHtml(JSON.stringify(cp.data, null, 2))}</div>` : ''}
|
|
1888
|
+
</div>
|
|
1889
|
+
`;
|
|
1890
|
+
}).join('')}
|
|
1753
1891
|
</div>
|
|
1754
|
-
`
|
|
1892
|
+
`}
|
|
1755
1893
|
</div>
|
|
1756
1894
|
</div>
|
|
1757
1895
|
`;
|
|
@@ -1759,6 +1897,43 @@
|
|
|
1759
1897
|
|
|
1760
1898
|
modalBody.innerHTML = html;
|
|
1761
1899
|
modalOverlay.classList.add('open');
|
|
1900
|
+
|
|
1901
|
+
// Attach tab switching handlers (DOM-only, no re-fetch)
|
|
1902
|
+
modalBody.querySelectorAll('.modal-tab').forEach(tab => {
|
|
1903
|
+
tab.addEventListener('click', () => {
|
|
1904
|
+
if (tab.hasAttribute('disabled')) return;
|
|
1905
|
+
const targetTab = tab.dataset.tab;
|
|
1906
|
+
|
|
1907
|
+
// Update tab active states
|
|
1908
|
+
modalBody.querySelectorAll('.modal-tab').forEach(t =>
|
|
1909
|
+
t.classList.toggle('active', t.dataset.tab === targetTab));
|
|
1910
|
+
|
|
1911
|
+
// Update content visibility
|
|
1912
|
+
modalBody.querySelectorAll('.modal-tab-content').forEach(c =>
|
|
1913
|
+
c.classList.toggle('active', c.dataset.tabContent === targetTab));
|
|
1914
|
+
|
|
1915
|
+
// Update global state for re-opens
|
|
1916
|
+
activeModalTab = targetTab;
|
|
1917
|
+
});
|
|
1918
|
+
});
|
|
1919
|
+
|
|
1920
|
+
// Attach "show more comments" handler if present
|
|
1921
|
+
const showMoreCommentsBtn = document.getElementById('showMoreComments');
|
|
1922
|
+
if (showMoreCommentsBtn) {
|
|
1923
|
+
showMoreCommentsBtn.addEventListener('click', () => {
|
|
1924
|
+
showAllComments = true;
|
|
1925
|
+
openTaskModal(taskId, true);
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
// Attach "show more checkpoints" handler if present
|
|
1930
|
+
const showMoreCheckpointsBtn = document.getElementById('showMoreCheckpoints');
|
|
1931
|
+
if (showMoreCheckpointsBtn) {
|
|
1932
|
+
showMoreCheckpointsBtn.addEventListener('click', () => {
|
|
1933
|
+
showAllCheckpoints = true;
|
|
1934
|
+
openTaskModal(taskId, true);
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1762
1937
|
} catch (error) {
|
|
1763
1938
|
console.error('Failed to load task:', error);
|
|
1764
1939
|
alert('Failed to load task details');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hzl-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Web dashboard for HZL - A Kanban-style task monitoring UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"visualization"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"hzl-core": "1.
|
|
55
|
+
"hzl-core": "1.20.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^20.10.0",
|