function MiningMonitorWidget(config = {}) {
const {
title = "Mining Machine Status Monitor",
initialStatuses = [],
colorMap = {},
showSummaryChart = true,
showFilteredTable = true,
maxTableEntries = 5,
machines = []
} = config;
const container = html`<div class="mining-monitor-widget"></div>`;
container.style.fontFamily = "system-ui, sans-serif";
container.style.maxWidth = "800px";
container.style.margin = "0 auto";
container.style.padding = "16px";
container.style.borderRadius = "8px";
container.style.boxShadow = "0 4px 12px rgba(0,0,0,0.1)";
container.style.backgroundColor = "#f8f9fa";
const titleEl = html`<h2 style="color: #343a40; margin: 0 0 12px 0;">${title}</h2>`;
const descEl = html`<p style="color: #6c757d; margin: 0 0 16px 0;">Filter by status and location:</p>`;
container.appendChild(titleEl);
container.appendChild(descEl);
const chartDiv = html`<div style="height: 40px; width: 100%; background: #e9ecef; margin-bottom: 20px; display: flex; border-radius: 4px;"></div>`;
const tableDiv = html`<div><table style="width: 100%; border-collapse: collapse;"><thead>
<tr>
<th>ID</th><th>Status</th><th>Temp</th><th>Hashrate</th><th>Uptime (min)</th><th>Location</th>
</tr></thead><tbody></tbody></table></div>`;
const statsDiv = html`<div style="display: flex; justify-content: space-around; margin-top: 20px;"></div>`;
container.appendChild(chartDiv);
container.appendChild(tableDiv);
container.appendChild(statsDiv);
let showAll = false;
function updateUI() {
const selectedStatuses = container.value?.status ?? initialStatuses;
const selectedLocation = container.value?.location ?? null;
const filtered = machines.filter(m =>
selectedStatuses.includes(m.status) &&
(!selectedLocation || m.location === selectedLocation)
);
chartDiv.innerHTML = "";
const total = machines.length;
initialStatuses.forEach(status => {
const count = machines.filter(m =>
m.status === status &&
(!selectedLocation || m.location === selectedLocation)
).length;
const width = total > 0 ? (count / total) * 100 : 0;
const bar = html`<div title="${status}: ${count}" style="height: 100%; background: ${colorMap[status]}; width: ${width}%;"></div>`;
chartDiv.appendChild(bar);
});
const body = tableDiv.querySelector("tbody");
body.innerHTML = "";
const displayed = showAll ? filtered : filtered.slice(0, maxTableEntries);
displayed.forEach(m => {
const row = html`<tr>
<td>${m.id}</td>
<td>${m.status}</td>
<td>${m.temperature}°C</td>
<td>${m.hashrate} MH/s</td>
<td>${m.uptime}</td>
<td>${m.location}</td>
</tr>`;
body.appendChild(row);
});
if (filtered.length > maxTableEntries) {
const toggleRow = html`<tr><td colspan="7" style="text-align: center;">
<button style="background: none; border: none; color: #007bff; cursor: pointer; font-size: 14px;">
${showAll ? "Show Less" : `+ ${filtered.length - maxTableEntries} more...`}
</button>
</td></tr>`;
toggleRow.querySelector("button").onclick = () => {
showAll = !showAll;
updateUI();
};
body.appendChild(toggleRow);
}
statsDiv.innerHTML = "";
const avg = (arr, key) => arr.reduce((sum, m) => sum + m[key], 0) / (arr.length || 1);
const statEl = (label, value) => html`<div><div style="font-size: 20px; font-weight: bold;">${value}</div><div style="font-size: 12px; color: #777;">${label}</div></div>`;
statsDiv.appendChild(statEl("Machines", `${filtered.length} / ${total}`));
statsDiv.appendChild(statEl("Avg Temp", `${avg(filtered, "temperature").toFixed(1)}°C`));
statsDiv.appendChild(statEl("Avg Hashrate", `${avg(filtered, "hashrate").toFixed(1)} MH/s`));
statsDiv.appendChild(statEl("Avg Uptime", `${avg(filtered, "uptime").toFixed(0)} min`));
}
return ReactiveWidget(container, {
value: {
status: initialStatuses,
location: null
},
showValue: updateUI
});
}