feature/monitoring-overview #10
@@ -141,7 +141,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="monitoring-view" class="view-content">
|
<div id="monitoring-view" class="view-content">
|
||||||
<div class="monitoring-section">
|
<div class="monitoring-view-section">
|
||||||
<div class="monitoring-header">
|
<div class="monitoring-header">
|
||||||
<h2>📊 Monitoring</h2>
|
<h2>📊 Monitoring</h2>
|
||||||
<button class="refresh-btn" id="refresh-monitoring-btn">
|
<button class="refresh-btn" id="refresh-monitoring-btn">
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ class MonitoringViewComponent extends Component {
|
|||||||
|
|
||||||
// Track if we've already loaded data to prevent unnecessary reloads
|
// Track if we've already loaded data to prevent unnecessary reloads
|
||||||
this.dataLoaded = false;
|
this.dataLoaded = false;
|
||||||
|
|
||||||
|
// Drawer state for desktop
|
||||||
|
this.drawer = new DrawerComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
mount() {
|
mount() {
|
||||||
@@ -75,6 +78,82 @@ class MonitoringViewComponent extends Component {
|
|||||||
await this.viewModel.refresh();
|
await this.viewModel.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if we should use desktop drawer behavior
|
||||||
|
isDesktop() {
|
||||||
|
return this.drawer.isDesktop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open drawer for a specific node
|
||||||
|
openDrawerForNode(nodeData) {
|
||||||
|
const { ip, hostname } = nodeData;
|
||||||
|
|
||||||
|
// Get display name for drawer title
|
||||||
|
let displayName = ip;
|
||||||
|
if (hostname && ip) {
|
||||||
|
displayName = `${hostname} - ${ip}`;
|
||||||
|
} else if (hostname) {
|
||||||
|
displayName = hostname;
|
||||||
|
} else if (ip) {
|
||||||
|
displayName = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open drawer with content callback
|
||||||
|
this.drawer.openDrawer(displayName, (contentContainer, setActiveComponent) => {
|
||||||
|
// Load and mount NodeDetails into drawer
|
||||||
|
const nodeDetailsVM = new NodeDetailsViewModel();
|
||||||
|
const nodeDetailsComponent = new NodeDetailsComponent(contentContainer, nodeDetailsVM, this.eventBus);
|
||||||
|
setActiveComponent(nodeDetailsComponent);
|
||||||
|
|
||||||
|
nodeDetailsVM.loadNodeDetails(ip).then(() => {
|
||||||
|
nodeDetailsComponent.mount();
|
||||||
|
}).catch((error) => {
|
||||||
|
logger.error('Failed to load node details for drawer:', error);
|
||||||
|
contentContainer.innerHTML = `
|
||||||
|
<div class="error">
|
||||||
|
<strong>Error loading node details:</strong><br>
|
||||||
|
${this.escapeHtml(error.message)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDrawer() {
|
||||||
|
this.drawer.closeDrawer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up resources
|
||||||
|
cleanup() {
|
||||||
|
if (this.drawer) {
|
||||||
|
this.drawer.cleanup();
|
||||||
|
}
|
||||||
|
super.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup click event listeners for node cards
|
||||||
|
setupNodeCardClickListeners(container) {
|
||||||
|
const nodeCards = container.querySelectorAll('.node-card');
|
||||||
|
nodeCards.forEach(card => {
|
||||||
|
const nodeIp = card.dataset.nodeIp;
|
||||||
|
if (nodeIp) {
|
||||||
|
// Find the node data
|
||||||
|
const nodeResources = this.viewModel.get('nodeResources');
|
||||||
|
const nodeData = nodeResources.get(nodeIp);
|
||||||
|
|
||||||
|
if (nodeData) {
|
||||||
|
this.addEventListener(card, 'click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.openDrawerForNode(nodeData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add hover cursor style
|
||||||
|
card.style.cursor = 'pointer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
logger.debug('MonitoringViewComponent: Rendering...');
|
logger.debug('MonitoringViewComponent: Rendering...');
|
||||||
|
|
||||||
@@ -226,6 +305,9 @@ class MonitoringViewComponent extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Add click event listeners to node cards
|
||||||
|
this.setupNodeCardClickListeners(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNodeCard(nodeData) {
|
renderNodeCard(nodeData) {
|
||||||
@@ -233,7 +315,7 @@ class MonitoringViewComponent extends Component {
|
|||||||
|
|
||||||
if (!hasResources) {
|
if (!hasResources) {
|
||||||
return `
|
return `
|
||||||
<div class="node-card error">
|
<div class="node-card error" data-node-ip="${ip}">
|
||||||
<div class="node-header">
|
<div class="node-header">
|
||||||
<div class="node-title">${hostname || ip}</div>
|
<div class="node-title">${hostname || ip}</div>
|
||||||
<div class="node-ip">${ip}</div>
|
<div class="node-ip">${ip}</div>
|
||||||
@@ -297,7 +379,7 @@ class MonitoringViewComponent extends Component {
|
|||||||
resourceSource === 'basic' ? '📋 Basic Data' : '❓ Unknown';
|
resourceSource === 'basic' ? '📋 Basic Data' : '❓ Unknown';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="node-card">
|
<div class="node-card" data-node-ip="${ip}">
|
||||||
<div class="node-header">
|
<div class="node-header">
|
||||||
<div class="node-title">${hostname || ip}</div>
|
<div class="node-title">${hostname || ip}</div>
|
||||||
<div class="node-ip">${ip}</div>
|
<div class="node-ip">${ip}</div>
|
||||||
|
|||||||
@@ -341,7 +341,6 @@ class NodeDetailsComponent extends Component {
|
|||||||
if (monitoringResources) {
|
if (monitoringResources) {
|
||||||
html += `
|
html += `
|
||||||
<div class="monitoring-section">
|
<div class="monitoring-section">
|
||||||
<div class="monitoring-header">Resources</div>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// CPU Usage
|
// CPU Usage
|
||||||
|
|||||||
@@ -3989,7 +3989,7 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Monitoring View Styles */
|
/* Monitoring View Styles */
|
||||||
.monitoring-section {
|
.monitoring-view-section {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
backdrop-filter: var(--backdrop-blur);
|
backdrop-filter: var(--backdrop-blur);
|
||||||
@@ -4001,7 +4001,7 @@ html {
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitoring-section::before {
|
.monitoring-view-section::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -4036,7 +4036,8 @@ html {
|
|||||||
|
|
||||||
/* Cluster Summary Styles */
|
/* Cluster Summary Styles */
|
||||||
.cluster-summary-content {
|
.cluster-summary-content {
|
||||||
/*background: var(--bg-tertiary);
|
/*
|
||||||
|
background: var(--bg-tertiary);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
border: 1px solid var(--border-primary);*/
|
border: 1px solid var(--border-primary);*/
|
||||||
@@ -4159,6 +4160,17 @@ html {
|
|||||||
.node-card:hover {
|
.node-card:hover {
|
||||||
border-color: rgba(255, 255, 255, 0.1);
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-card[data-node-ip] {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-card[data-node-ip]:active {
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.node-card.error {
|
.node-card.error {
|
||||||
@@ -4272,6 +4284,20 @@ html {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Monitoring Section in Node Details Drawer */
|
||||||
|
.details-drawer .monitoring-section {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-drawer .monitoring-section .monitoring-header {
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.summary-stats {
|
.summary-stats {
|
||||||
|
|||||||
Reference in New Issue
Block a user