@zintrust/workers 0.1.28 → 0.1.30
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/README.md +16 -1
- package/dist/AnomalyDetection.d.ts +4 -0
- package/dist/AnomalyDetection.js +8 -0
- package/dist/BroadcastWorker.d.ts +2 -0
- package/dist/CanaryController.js +49 -5
- package/dist/ChaosEngineering.js +13 -0
- package/dist/ClusterLock.js +21 -10
- package/dist/DeadLetterQueue.js +12 -8
- package/dist/MultiQueueWorker.d.ts +1 -1
- package/dist/MultiQueueWorker.js +12 -7
- package/dist/NotificationWorker.d.ts +2 -0
- package/dist/PriorityQueue.d.ts +2 -2
- package/dist/PriorityQueue.js +20 -21
- package/dist/ResourceMonitor.js +65 -38
- package/dist/WorkerFactory.d.ts +23 -3
- package/dist/WorkerFactory.js +420 -40
- package/dist/WorkerInit.js +8 -3
- package/dist/WorkerMetrics.d.ts +2 -1
- package/dist/WorkerMetrics.js +152 -93
- package/dist/WorkerRegistry.d.ts +6 -0
- package/dist/WorkerRegistry.js +70 -1
- package/dist/WorkerShutdown.d.ts +21 -0
- package/dist/WorkerShutdown.js +82 -9
- package/dist/WorkerShutdownDurableObject.d.ts +12 -0
- package/dist/WorkerShutdownDurableObject.js +41 -0
- package/dist/build-manifest.json +171 -99
- package/dist/createQueueWorker.d.ts +2 -0
- package/dist/createQueueWorker.js +42 -27
- package/dist/dashboard/types.d.ts +5 -0
- package/dist/dashboard/workers-api.js +136 -43
- package/dist/http/WorkerApiController.js +1 -0
- package/dist/http/WorkerController.js +133 -85
- package/dist/http/WorkerMonitoringService.d.ts +11 -0
- package/dist/http/WorkerMonitoringService.js +62 -0
- package/dist/http/middleware/CustomValidation.js +1 -1
- package/dist/http/middleware/EditWorkerValidation.d.ts +1 -1
- package/dist/http/middleware/EditWorkerValidation.js +7 -6
- package/dist/http/middleware/ProcessorPathSanitizer.js +101 -35
- package/dist/http/middleware/WorkerValidationChain.js +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/routes/workers.js +48 -6
- package/dist/storage/WorkerStore.d.ts +4 -1
- package/dist/storage/WorkerStore.js +55 -7
- package/dist/telemetry/api/TelemetryAPI.d.ts +46 -0
- package/dist/telemetry/api/TelemetryAPI.js +219 -0
- package/dist/telemetry/api/TelemetryMonitoringService.d.ts +17 -0
- package/dist/telemetry/api/TelemetryMonitoringService.js +113 -0
- package/dist/telemetry/components/AlertPanel.d.ts +1 -0
- package/dist/telemetry/components/AlertPanel.js +13 -0
- package/dist/telemetry/components/CostTracking.d.ts +1 -0
- package/dist/telemetry/components/CostTracking.js +14 -0
- package/dist/telemetry/components/ResourceUsageChart.d.ts +1 -0
- package/dist/telemetry/components/ResourceUsageChart.js +11 -0
- package/dist/telemetry/components/WorkerHealthChart.d.ts +1 -0
- package/dist/telemetry/components/WorkerHealthChart.js +11 -0
- package/dist/telemetry/index.d.ts +15 -0
- package/dist/telemetry/index.js +60 -0
- package/dist/telemetry/routes/dashboard.d.ts +6 -0
- package/dist/telemetry/routes/dashboard.js +608 -0
- package/dist/ui/router/EmbeddedAssets.d.ts +4 -0
- package/dist/ui/router/EmbeddedAssets.js +13 -0
- package/dist/ui/router/ui.js +100 -4
- package/package.json +9 -5
- package/src/AnomalyDetection.ts +9 -0
- package/src/CanaryController.ts +41 -5
- package/src/ChaosEngineering.ts +14 -0
- package/src/ClusterLock.ts +22 -9
- package/src/DeadLetterQueue.ts +13 -8
- package/src/MultiQueueWorker.ts +15 -8
- package/src/PriorityQueue.ts +21 -22
- package/src/ResourceMonitor.ts +72 -40
- package/src/WorkerFactory.ts +545 -49
- package/src/WorkerInit.ts +8 -3
- package/src/WorkerMetrics.ts +183 -105
- package/src/WorkerRegistry.ts +80 -1
- package/src/WorkerShutdown.ts +115 -9
- package/src/WorkerShutdownDurableObject.ts +64 -0
- package/src/createQueueWorker.ts +73 -30
- package/src/dashboard/types.ts +5 -0
- package/src/dashboard/workers-api.ts +165 -52
- package/src/http/WorkerApiController.ts +1 -0
- package/src/http/WorkerController.ts +167 -90
- package/src/http/WorkerMonitoringService.ts +77 -0
- package/src/http/middleware/CustomValidation.ts +1 -1
- package/src/http/middleware/EditWorkerValidation.ts +7 -6
- package/src/http/middleware/ProcessorPathSanitizer.ts +123 -36
- package/src/http/middleware/WorkerValidationChain.ts +1 -0
- package/src/index.ts +6 -1
- package/src/routes/workers.ts +66 -9
- package/src/storage/WorkerStore.ts +59 -9
- package/src/telemetry/api/TelemetryAPI.ts +292 -0
- package/src/telemetry/api/TelemetryMonitoringService.ts +149 -0
- package/src/telemetry/components/AlertPanel.ts +13 -0
- package/src/telemetry/components/CostTracking.ts +14 -0
- package/src/telemetry/components/ResourceUsageChart.ts +11 -0
- package/src/telemetry/components/WorkerHealthChart.ts +11 -0
- package/src/telemetry/index.ts +121 -0
- package/src/telemetry/public/assets/zintrust-logo.svg +15 -0
- package/src/telemetry/routes/dashboard.ts +638 -0
- package/src/telemetry/styles/tailwind.css +1 -0
- package/src/telemetry/styles/zintrust-theme.css +8 -0
- package/src/ui/router/EmbeddedAssets.ts +13 -0
- package/src/ui/router/ui.ts +112 -5
- package/src/ui/workers/index.html +2 -2
- package/src/ui/workers/main.js +232 -61
- package/src/ui/workers/zintrust.svg +30 -0
- package/dist/dashboard/workers-dashboard-ui.d.ts +0 -3
- package/dist/dashboard/workers-dashboard-ui.js +0 -1026
- package/dist/dashboard/workers-dashboard.d.ts +0 -4
- package/dist/dashboard/workers-dashboard.js +0 -904
package/src/ui/workers/main.js
CHANGED
|
@@ -16,6 +16,9 @@ let totalWorkers = 0;
|
|
|
16
16
|
let autoRefreshEnabled = true;
|
|
17
17
|
let refreshTimer = null;
|
|
18
18
|
let currentTheme = null;
|
|
19
|
+
let eventSource = null;
|
|
20
|
+
let sseActive = false;
|
|
21
|
+
let lastSseRefresh = 0;
|
|
19
22
|
const _bulkAutoStartEnabled = false;
|
|
20
23
|
const _lastWorkers = [];
|
|
21
24
|
const detailsCache = new Map();
|
|
@@ -88,7 +91,7 @@ function validateWorkerData(data) {
|
|
|
88
91
|
return true;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
// Data fetching
|
|
94
|
+
// Data fetching - only for search and pagination, SSE handles regular updates
|
|
92
95
|
async function fetchData() {
|
|
93
96
|
const elements = getDomElements();
|
|
94
97
|
|
|
@@ -105,7 +108,7 @@ async function fetchData() {
|
|
|
105
108
|
limit: limit,
|
|
106
109
|
status: document.getElementById('status-filter')?.value || '',
|
|
107
110
|
driver: document.getElementById('driver-filter')?.value || '',
|
|
108
|
-
sortBy: document.getElementById('sort-select')?.value || '
|
|
111
|
+
sortBy: document.getElementById('sort-select')?.value || 'status',
|
|
109
112
|
sortOrder: 'asc',
|
|
110
113
|
search: query,
|
|
111
114
|
});
|
|
@@ -138,7 +141,7 @@ async function fetchData() {
|
|
|
138
141
|
function changeLimit(_newLimit) {
|
|
139
142
|
localStorage.setItem(PAGE_SIZE_KEY, _newLimit);
|
|
140
143
|
currentPage = 1;
|
|
141
|
-
fetchData();
|
|
144
|
+
fetchData(); // Enable for pagination
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
// Make functions globally available for HTML onclick/onchange handlers
|
|
@@ -308,7 +311,9 @@ function updateDetailViews(detailRow, details) {
|
|
|
308
311
|
if (!details) return;
|
|
309
312
|
|
|
310
313
|
// Update all data-key elements
|
|
311
|
-
detailRow.querySelectorAll('[data-key]').forEach(
|
|
314
|
+
detailRow.querySelectorAll('[data-key]').forEach((element) => {
|
|
315
|
+
updateDetailElement(element);
|
|
316
|
+
});
|
|
312
317
|
|
|
313
318
|
// Delegate to specialized functions
|
|
314
319
|
updateLogsContainer(detailRow, details);
|
|
@@ -1236,41 +1241,79 @@ function loadPage(direction) {
|
|
|
1236
1241
|
} else if (direction === 'next' && currentPage < totalPages) {
|
|
1237
1242
|
currentPage++;
|
|
1238
1243
|
}
|
|
1239
|
-
fetchData();
|
|
1244
|
+
fetchData(); // Enable for pagination
|
|
1240
1245
|
}
|
|
1241
1246
|
|
|
1242
1247
|
function goToPage(page) {
|
|
1243
1248
|
currentPage = page;
|
|
1244
|
-
fetchData();
|
|
1249
|
+
fetchData(); // Enable for pagination
|
|
1245
1250
|
}
|
|
1246
1251
|
|
|
1247
1252
|
// Worker actions
|
|
1248
1253
|
async function startWorker(name, driver) {
|
|
1254
|
+
// Find and disable the start button to prevent multiple clicks
|
|
1255
|
+
const startBtn = document.querySelector(`button[onclick="startWorker('${name}', '${driver}')"]`);
|
|
1256
|
+
if (startBtn) {
|
|
1257
|
+
startBtn.disabled = true;
|
|
1258
|
+
startBtn.textContent = 'Starting...';
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1249
1261
|
try {
|
|
1250
1262
|
await fetch(`${API_BASE}/api/workers/${name}/start?driver=${driver}`, { method: 'POST' });
|
|
1251
|
-
fetchData();
|
|
1263
|
+
fetchData(); // Refresh data after action
|
|
1252
1264
|
} catch (err) {
|
|
1253
1265
|
console.error('Failed to start worker:', err);
|
|
1266
|
+
// Re-enable button on error
|
|
1267
|
+
if (startBtn) {
|
|
1268
|
+
startBtn.disabled = false;
|
|
1269
|
+
startBtn.textContent = 'Start';
|
|
1270
|
+
}
|
|
1254
1271
|
}
|
|
1255
1272
|
}
|
|
1256
1273
|
|
|
1257
1274
|
async function stopWorker(name, driver) {
|
|
1275
|
+
// Find and disable the stop button to prevent multiple clicks
|
|
1276
|
+
const stopBtn = document.querySelector(`button[onclick="stopWorker('${name}', '${driver}')"]`);
|
|
1277
|
+
if (stopBtn) {
|
|
1278
|
+
stopBtn.disabled = true;
|
|
1279
|
+
stopBtn.textContent = 'Stopping...';
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1258
1282
|
try {
|
|
1259
1283
|
await fetch(`${API_BASE}/api/workers/${name}/stop?driver=${driver}`, { method: 'POST' });
|
|
1260
|
-
fetchData();
|
|
1284
|
+
fetchData(); // Refresh data after action
|
|
1261
1285
|
} catch (err) {
|
|
1262
1286
|
console.error('Failed to stop worker:', err);
|
|
1287
|
+
// Re-enable button on error
|
|
1288
|
+
if (stopBtn) {
|
|
1289
|
+
stopBtn.disabled = false;
|
|
1290
|
+
stopBtn.textContent = 'Stop';
|
|
1291
|
+
}
|
|
1263
1292
|
}
|
|
1264
1293
|
}
|
|
1265
1294
|
|
|
1266
1295
|
async function restartWorker(name, driver) {
|
|
1296
|
+
// Find and disable the restart button to prevent multiple clicks
|
|
1297
|
+
const restartBtn = document.querySelector(
|
|
1298
|
+
`button[onclick="restartWorker('${name}', '${driver}')"]`
|
|
1299
|
+
);
|
|
1300
|
+
if (restartBtn) {
|
|
1301
|
+
restartBtn.disabled = true;
|
|
1302
|
+
restartBtn.textContent = 'Restarting...';
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1267
1305
|
try {
|
|
1268
1306
|
await fetch(`${API_BASE}/api/workers/${name}/restart?driver=${driver}`, {
|
|
1269
1307
|
method: 'POST',
|
|
1270
1308
|
});
|
|
1271
|
-
fetchData();
|
|
1309
|
+
fetchData(); // Refresh data after action
|
|
1272
1310
|
} catch (err) {
|
|
1273
1311
|
console.error('Failed to restart worker:', err);
|
|
1312
|
+
// Re-enable button on error
|
|
1313
|
+
if (restartBtn) {
|
|
1314
|
+
restartBtn.disabled = false;
|
|
1315
|
+
restartBtn.textContent = 'Restart';
|
|
1316
|
+
}
|
|
1274
1317
|
}
|
|
1275
1318
|
}
|
|
1276
1319
|
|
|
@@ -1283,7 +1326,7 @@ async function deleteWorker(name, driver) {
|
|
|
1283
1326
|
method: 'DELETE',
|
|
1284
1327
|
});
|
|
1285
1328
|
if (!response.ok) throw new Error('Failed to delete worker');
|
|
1286
|
-
fetchData();
|
|
1329
|
+
fetchData(); // Refresh data after action
|
|
1287
1330
|
} catch (err) {
|
|
1288
1331
|
console.error('Failed to delete worker:', err);
|
|
1289
1332
|
alert('Failed to delete worker: ' + err.message);
|
|
@@ -1608,7 +1651,7 @@ function createEditButtons(modal, textarea, name, driver) {
|
|
|
1608
1651
|
|
|
1609
1652
|
alert('Worker updated successfully!');
|
|
1610
1653
|
modal.remove();
|
|
1611
|
-
fetchData(); // Refresh
|
|
1654
|
+
fetchData(); // Refresh data after action
|
|
1612
1655
|
} catch (error) {
|
|
1613
1656
|
alert('Invalid JSON: ' + error.message);
|
|
1614
1657
|
}
|
|
@@ -1683,62 +1726,171 @@ function toggleAutoRefresh() {
|
|
|
1683
1726
|
setAutoRefresh(!autoRefreshEnabled);
|
|
1684
1727
|
}
|
|
1685
1728
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1729
|
+
function setupEventStream() {
|
|
1730
|
+
if (!globalThis.window.EventSource) return;
|
|
1731
|
+
|
|
1732
|
+
if (eventSource) {
|
|
1733
|
+
eventSource.close();
|
|
1734
|
+
eventSource = null;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// Get current sort parameters to match fetchData
|
|
1738
|
+
const sortBy = document.getElementById('sort-select')?.value || 'status';
|
|
1739
|
+
const sortOrder = 'asc';
|
|
1740
|
+
const status = document.getElementById('status-filter')?.value || '';
|
|
1741
|
+
const driver = document.getElementById('driver-filter')?.value || '';
|
|
1742
|
+
const search = document.getElementById('search-input')?.value || '';
|
|
1743
|
+
|
|
1744
|
+
const params = new URLSearchParams({
|
|
1745
|
+
sortBy,
|
|
1746
|
+
sortOrder,
|
|
1747
|
+
status,
|
|
1748
|
+
driver,
|
|
1749
|
+
search,
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
eventSource = new globalThis.window.EventSource(
|
|
1753
|
+
API_BASE + '/api/workers/events?' + params.toString()
|
|
1754
|
+
);
|
|
1690
1755
|
|
|
1756
|
+
eventSource.onopen = () => {
|
|
1757
|
+
sseActive = true;
|
|
1758
|
+
if (refreshTimer) {
|
|
1759
|
+
clearInterval(refreshTimer);
|
|
1760
|
+
refreshTimer = null;
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
eventSource.onmessage = (evt) => {
|
|
1765
|
+
const elements = getDomElements();
|
|
1766
|
+
|
|
1767
|
+
try {
|
|
1768
|
+
const payload = JSON.parse(evt.data);
|
|
1769
|
+
if (payload && payload.type === 'snapshot') {
|
|
1770
|
+
const now = Date.now();
|
|
1771
|
+
if (now - lastSseRefresh < 4000) return;
|
|
1772
|
+
lastSseRefresh = now;
|
|
1773
|
+
// Use SSE data to update the page
|
|
1774
|
+
if (payload.workers) {
|
|
1775
|
+
renderWorkers(payload.workers);
|
|
1776
|
+
// Hide loading state if it's showing
|
|
1777
|
+
} else if (payload.snapshot) {
|
|
1778
|
+
// Handle queue snapshot events (different format)
|
|
1779
|
+
// console.log('Queue snapshot received, not updating workers UI');
|
|
1780
|
+
}
|
|
1781
|
+
if (payload.monitoring) {
|
|
1782
|
+
// Handle queue monitoring events (different format)
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
hideLoadingState(elements);
|
|
1786
|
+
} catch (err) {
|
|
1787
|
+
hideLoadingState(elements);
|
|
1788
|
+
console.error('Failed to parse SSE payload', err);
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
|
|
1792
|
+
eventSource.onerror = () => {
|
|
1793
|
+
if (eventSource) {
|
|
1794
|
+
eventSource.close();
|
|
1795
|
+
eventSource = null;
|
|
1796
|
+
}
|
|
1797
|
+
sseActive = false;
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// Helper functions to reduce complexity
|
|
1802
|
+
function clearRefreshTimer() {
|
|
1691
1803
|
if (refreshTimer) {
|
|
1692
1804
|
clearInterval(refreshTimer);
|
|
1693
1805
|
refreshTimer = null;
|
|
1694
1806
|
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
function disableEventStream() {
|
|
1810
|
+
if (eventSource) {
|
|
1811
|
+
eventSource.close();
|
|
1812
|
+
eventSource = null;
|
|
1813
|
+
}
|
|
1814
|
+
sseActive = false;
|
|
1815
|
+
}
|
|
1695
1816
|
|
|
1696
|
-
|
|
1697
|
-
|
|
1817
|
+
function enableEventStreamOrPolling() {
|
|
1818
|
+
if (globalThis.window.EventSource) {
|
|
1819
|
+
setupEventStream();
|
|
1820
|
+
} else if (!sseActive && autoRefreshEnabled) {
|
|
1821
|
+
refreshTimer = setInterval(fetchData, 30000); // Commented out - SSE should be primary
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
function createPauseIcon(icon) {
|
|
1826
|
+
// Clear existing content
|
|
1827
|
+
while (icon.firstChild) {
|
|
1828
|
+
icon.firstChild.remove();
|
|
1829
|
+
}
|
|
1830
|
+
// Create pause icon
|
|
1831
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1832
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
1833
|
+
const rect1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
1834
|
+
rect1.setAttribute('x', '6');
|
|
1835
|
+
rect1.setAttribute('y', '4');
|
|
1836
|
+
rect1.setAttribute('width', '4');
|
|
1837
|
+
rect1.setAttribute('height', '16');
|
|
1838
|
+
const rect2 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
1839
|
+
rect2.setAttribute('x', '14');
|
|
1840
|
+
rect2.setAttribute('y', '4');
|
|
1841
|
+
rect2.setAttribute('width', '4');
|
|
1842
|
+
rect2.setAttribute('height', '16');
|
|
1843
|
+
svg.appendChild(rect1);
|
|
1844
|
+
svg.appendChild(rect2);
|
|
1845
|
+
icon.appendChild(svg);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
function createPlayIcon(icon) {
|
|
1849
|
+
// Clear existing content
|
|
1850
|
+
while (icon.firstChild) {
|
|
1851
|
+
icon.firstChild.remove();
|
|
1698
1852
|
}
|
|
1853
|
+
// Create play icon
|
|
1854
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1855
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
1856
|
+
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
1857
|
+
polygon.setAttribute('points', '5 3 19 12 5 21 5 3');
|
|
1858
|
+
svg.appendChild(polygon);
|
|
1859
|
+
icon.appendChild(svg);
|
|
1860
|
+
}
|
|
1699
1861
|
|
|
1862
|
+
function updateRefreshButton(enabled) {
|
|
1700
1863
|
const btn = document.getElementById('auto-refresh-toggle');
|
|
1701
1864
|
const icon = document.getElementById('auto-refresh-icon');
|
|
1702
1865
|
const label = document.getElementById('auto-refresh-label');
|
|
1703
1866
|
|
|
1704
|
-
if (btn
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
svg.appendChild(rect1);
|
|
1725
|
-
svg.appendChild(rect2);
|
|
1726
|
-
icon.appendChild(svg);
|
|
1727
|
-
} else {
|
|
1728
|
-
label.textContent = 'Auto Refresh';
|
|
1729
|
-
// Clear existing content
|
|
1730
|
-
while (icon.firstChild) {
|
|
1731
|
-
icon.firstChild.remove();
|
|
1732
|
-
}
|
|
1733
|
-
// Create play icon
|
|
1734
|
-
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1735
|
-
svg.setAttribute('viewBox', '0 0 24 24');
|
|
1736
|
-
const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
|
1737
|
-
polygon.setAttribute('points', '5 3 19 12 5 21 5 3');
|
|
1738
|
-
svg.appendChild(polygon);
|
|
1739
|
-
icon.appendChild(svg);
|
|
1740
|
-
}
|
|
1867
|
+
if (!btn || !icon || !label) return;
|
|
1868
|
+
|
|
1869
|
+
if (enabled) {
|
|
1870
|
+
label.textContent = 'Pause Refresh';
|
|
1871
|
+
createPauseIcon(icon);
|
|
1872
|
+
} else {
|
|
1873
|
+
label.textContent = 'Auto Refresh';
|
|
1874
|
+
createPlayIcon(icon);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// Auto-refresh - Refactored to reduce complexity
|
|
1879
|
+
function setAutoRefresh(enabled) {
|
|
1880
|
+
autoRefreshEnabled = enabled;
|
|
1881
|
+
localStorage.setItem(AUTO_REFRESH_KEY, enabled.toString());
|
|
1882
|
+
|
|
1883
|
+
clearRefreshTimer();
|
|
1884
|
+
|
|
1885
|
+
if (!enabled) {
|
|
1886
|
+
disableEventStream();
|
|
1741
1887
|
}
|
|
1888
|
+
|
|
1889
|
+
if (enabled) {
|
|
1890
|
+
enableEventStreamOrPolling();
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
updateRefreshButton(enabled);
|
|
1742
1894
|
}
|
|
1743
1895
|
|
|
1744
1896
|
// Event listeners
|
|
@@ -1749,21 +1901,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
1749
1901
|
document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
|
|
1750
1902
|
|
|
1751
1903
|
// Set up event listeners
|
|
1752
|
-
document.getElementById('status-filter').addEventListener('change',
|
|
1753
|
-
|
|
1754
|
-
|
|
1904
|
+
document.getElementById('status-filter').addEventListener('change', () => {
|
|
1905
|
+
currentPage = 1;
|
|
1906
|
+
setupEventStream(); // Reconnect SSE with new filters
|
|
1907
|
+
fetchData(); // Enable for search/filter
|
|
1908
|
+
});
|
|
1909
|
+
document.getElementById('driver-filter').addEventListener('change', () => {
|
|
1910
|
+
currentPage = 1;
|
|
1911
|
+
setupEventStream(); // Reconnect SSE with new filters
|
|
1912
|
+
fetchData(); // Enable for search/filter
|
|
1913
|
+
});
|
|
1914
|
+
document.getElementById('sort-select').addEventListener('change', () => {
|
|
1915
|
+
currentPage = 1;
|
|
1916
|
+
setupEventStream(); // Reconnect SSE with new sort
|
|
1917
|
+
fetchData(); // Enable for sorting
|
|
1918
|
+
});
|
|
1755
1919
|
|
|
1756
1920
|
const searchBtn = document.getElementById('search-btn');
|
|
1757
1921
|
if (searchBtn) {
|
|
1758
1922
|
searchBtn.addEventListener('click', () => {
|
|
1759
1923
|
currentPage = 1;
|
|
1760
|
-
|
|
1924
|
+
setupEventStream(); // Reconnect SSE with new search
|
|
1925
|
+
fetchData(); // Enable for search
|
|
1761
1926
|
});
|
|
1762
1927
|
}
|
|
1763
1928
|
document.getElementById('search-input').addEventListener('keypress', (e) => {
|
|
1764
1929
|
if (e.key === 'Enter') {
|
|
1765
1930
|
currentPage = 1;
|
|
1766
|
-
|
|
1931
|
+
setupEventStream(); // Reconnect SSE with new search
|
|
1932
|
+
fetchData(); // Enable for search
|
|
1767
1933
|
}
|
|
1768
1934
|
});
|
|
1769
1935
|
|
|
@@ -1776,6 +1942,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
1776
1942
|
// Use stored value
|
|
1777
1943
|
setAutoRefresh(storedAutoRefresh === 'true');
|
|
1778
1944
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1945
|
+
setupEventStream();
|
|
1946
|
+
// SSE should handle initial data loading
|
|
1947
|
+
globalThis.window.addEventListener('beforeunload', () => {
|
|
1948
|
+
if (eventSource) {
|
|
1949
|
+
eventSource.close();
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1781
1952
|
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<svg width="120" height="120" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="zt-g2d" x1="10" y1="50" x2="90" y2="50" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#22c55e" />
|
|
5
|
+
<stop offset="1" stop-color="#38bdf8" />
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<circle cx="50" cy="50" r="34" stroke="rgba(255,255,255,0.16)" stroke-width="4" />
|
|
9
|
+
<ellipse cx="50" cy="50" rx="40" ry="18" stroke="url(#zt-g2d)" stroke-width="4" />
|
|
10
|
+
<ellipse cx="50" cy="50" rx="18" ry="40" stroke="url(#zt-g2d)" stroke-width="4" opacity="0.75" />
|
|
11
|
+
<circle cx="50" cy="50" r="6" fill="url(#zt-g2d)" />
|
|
12
|
+
<path
|
|
13
|
+
d="M40 52C35 52 32 49 32 44C32 39 35 36 40 36H48"
|
|
14
|
+
stroke="white"
|
|
15
|
+
stroke-width="6"
|
|
16
|
+
stroke-linecap="round"
|
|
17
|
+
/>
|
|
18
|
+
<path
|
|
19
|
+
d="M60 48C65 48 68 51 68 56C68 61 65 64 60 64H52"
|
|
20
|
+
stroke="white"
|
|
21
|
+
stroke-width="6"
|
|
22
|
+
stroke-linecap="round"
|
|
23
|
+
/>
|
|
24
|
+
<path
|
|
25
|
+
d="M44 50H56"
|
|
26
|
+
stroke="rgba(255,255,255,0.22)"
|
|
27
|
+
stroke-width="6"
|
|
28
|
+
stroke-linecap="round"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|