feat: improve styling
This commit is contained in:
@@ -2905,8 +2905,10 @@ class MemberCardOverlayComponent extends Component {
|
||||
|
||||
setupEventListeners() {
|
||||
// Close overlay when clicking outside or pressing escape
|
||||
this.addEventListener(document, 'click', (e) => {
|
||||
if (this.isVisible && !this.container.contains(e.target)) {
|
||||
this.addEventListener(this.container, 'click', (e) => {
|
||||
if (!this.isVisible) return;
|
||||
// Only close when clicking on the backdrop, not inside the dialog content
|
||||
if (e.target === this.container) {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
@@ -2936,6 +2938,8 @@ class MemberCardOverlayComponent extends Component {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
hide() {
|
||||
this.isVisible = false;
|
||||
this.container.classList.remove('visible');
|
||||
@@ -2977,11 +2981,9 @@ class MemberCardOverlayComponent extends Component {
|
||||
<span class="latency-label">Latency:</span>
|
||||
<span class="latency-value">${member.latency ? member.latency + 'ms' : 'N/A'}</span>
|
||||
</div>
|
||||
${member.labels && Object.keys(member.labels).length ? `
|
||||
<div class="member-labels">
|
||||
${Object.entries(member.labels).map(([key, value]) => `<span class="label-chip">${key}: ${value}</span>`).join('')}
|
||||
<div class="member-labels" style="display: none;">
|
||||
<!-- Labels will be populated dynamically from node status API -->
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-details">
|
||||
@@ -3024,39 +3026,49 @@ class MemberCardOverlayComponent extends Component {
|
||||
// Load node details
|
||||
await nodeDetailsVM.loadNodeDetails(memberIp);
|
||||
|
||||
// Update the labels in the member header with the actual node status data
|
||||
const nodeStatus = nodeDetailsVM.get('nodeStatus');
|
||||
if (nodeStatus && nodeStatus.labels) {
|
||||
const labelsContainer = card.querySelector('.member-labels');
|
||||
if (labelsContainer) {
|
||||
// Update existing labels container and show it
|
||||
labelsContainer.innerHTML = Object.entries(nodeStatus.labels)
|
||||
.map(([key, value]) => `<span class="label-chip">${key}: ${value}</span>`)
|
||||
.join('');
|
||||
labelsContainer.style.display = 'block';
|
||||
} else {
|
||||
// Create new labels container if it doesn't exist
|
||||
const memberInfo = card.querySelector('.member-info');
|
||||
if (memberInfo) {
|
||||
const labelsDiv = document.createElement('div');
|
||||
labelsDiv.className = 'member-labels';
|
||||
labelsDiv.innerHTML = Object.entries(nodeStatus.labels)
|
||||
.map(([key, value]) => `<span class="label-chip">${key}: ${value}</span>`)
|
||||
.join('');
|
||||
|
||||
// Insert after latency
|
||||
const latencyDiv = memberInfo.querySelector('.member-latency');
|
||||
if (latencyDiv) {
|
||||
latencyDiv.parentNode.insertBefore(labelsDiv, latencyDiv.nextSibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mount the component
|
||||
nodeDetailsComponent.mount();
|
||||
|
||||
// Update UI
|
||||
card.classList.add('expanded');
|
||||
const expandIcon = card.querySelector('.expand-icon');
|
||||
if (expandIcon) {
|
||||
expandIcon.classList.add('expanded');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to expand card:', error);
|
||||
memberDetails.innerHTML = `
|
||||
<div class="error">
|
||||
<strong>Error loading node details:</strong><br>
|
||||
${error.message}
|
||||
</div>
|
||||
`;
|
||||
console.error('Failed to expand member card:', error);
|
||||
// Still show the UI even if details fail to load
|
||||
card.classList.add('expanded');
|
||||
const details = card.querySelector('.member-details');
|
||||
if (details) {
|
||||
details.innerHTML = '<div class="error">Failed to load node details</div>';
|
||||
}
|
||||
}
|
||||
|
||||
collapseCard(card, expandIcon) {
|
||||
card.classList.remove('expanded');
|
||||
if (expandIcon) {
|
||||
expandIcon.classList.remove('expanded');
|
||||
}
|
||||
|
||||
// Reset member details to loading state
|
||||
const memberDetails = card.querySelector('.member-details');
|
||||
if (memberDetails) {
|
||||
memberDetails.innerHTML = '<div class="loading-details">Loading detailed information...</div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,7 +17,13 @@ window.demoMembersData = {
|
||||
api: [
|
||||
{ uri: "/api/node/status", method: "GET" },
|
||||
{ uri: "/api/tasks/status", method: "GET" }
|
||||
]
|
||||
],
|
||||
labels: {
|
||||
environment: "production",
|
||||
region: "us-west",
|
||||
role: "worker",
|
||||
cluster: "spore-main"
|
||||
}
|
||||
},
|
||||
{
|
||||
hostname: "spore-node-2",
|
||||
@@ -35,7 +41,13 @@ window.demoMembersData = {
|
||||
api: [
|
||||
{ uri: "/api/node/status", method: "GET" },
|
||||
{ uri: "/api/tasks/status", method: "GET" }
|
||||
]
|
||||
],
|
||||
labels: {
|
||||
environment: "production",
|
||||
region: "us-west",
|
||||
role: "controller",
|
||||
cluster: "spore-main"
|
||||
}
|
||||
},
|
||||
{
|
||||
hostname: "spore-node-3",
|
||||
@@ -52,7 +64,13 @@ window.demoMembersData = {
|
||||
},
|
||||
api: [
|
||||
{ uri: "/api/node/status", method: "GET" }
|
||||
]
|
||||
],
|
||||
labels: {
|
||||
environment: "staging",
|
||||
region: "us-west",
|
||||
role: "worker",
|
||||
cluster: "spore-main"
|
||||
}
|
||||
},
|
||||
{
|
||||
hostname: "spore-node-4",
|
||||
@@ -71,7 +89,13 @@ window.demoMembersData = {
|
||||
{ uri: "/api/node/status", method: "GET" },
|
||||
{ uri: "/api/tasks/status", method: "GET" },
|
||||
{ uri: "/api/capabilities", method: "GET" }
|
||||
]
|
||||
],
|
||||
labels: {
|
||||
environment: "production",
|
||||
region: "us-east",
|
||||
role: "gateway",
|
||||
cluster: "spore-main"
|
||||
}
|
||||
},
|
||||
{
|
||||
hostname: "spore-node-5",
|
||||
@@ -86,7 +110,8 @@ window.demoMembersData = {
|
||||
cpuFreqMHz: 0,
|
||||
flashChipSize: 1048576
|
||||
},
|
||||
api: []
|
||||
api: [],
|
||||
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -120,5 +145,30 @@ window.demoApiClient = {
|
||||
];
|
||||
|
||||
return { members: members.filter(m => m.ip !== ip) };
|
||||
},
|
||||
|
||||
async getNodeStatus(ip) {
|
||||
// Simulate network delay
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
// Find the member by IP
|
||||
const member = window.demoMembersData.members.find(m => m.ip === ip);
|
||||
if (!member) {
|
||||
throw new Error('Node not found');
|
||||
}
|
||||
|
||||
// Return node status with labels
|
||||
return {
|
||||
...member.resources,
|
||||
api: member.api,
|
||||
labels: {
|
||||
environment: ip.includes('103') ? 'production' : 'production',
|
||||
region: ip.includes('103') ? 'us-east' : 'us-west',
|
||||
role: ip.includes('101') ? 'controller' : ip.includes('103') ? 'gateway' : 'worker',
|
||||
cluster: 'spore-main',
|
||||
nodeType: ip.includes('102') ? 'staging' : 'production',
|
||||
location: ip.includes('103') ? 'datacenter-2' : 'datacenter-1'
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -229,17 +229,17 @@ p {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.label-chip {
|
||||
.label-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.15rem 0.45rem;
|
||||
padding: 0.25rem 0.6rem;
|
||||
border-radius: 9999px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
background: rgba(30, 58, 138, 0.35);
|
||||
border: 1px solid rgba(59, 130, 246, 0.4);
|
||||
color: #dbeafe;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.member-card::before {
|
||||
content: '';
|
||||
@@ -2164,9 +2164,9 @@ p {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding-right: 0.25rem;
|
||||
background: rgba(139, 92, 246, 0.15);
|
||||
border: 1px solid rgba(139, 92, 246, 0.35);
|
||||
padding-right: 0.35rem;
|
||||
background: rgba(30, 58, 138, 0.35);
|
||||
border: 1px solid rgba(59, 130, 246, 0.55);
|
||||
}
|
||||
|
||||
.label-chip .chip-remove {
|
||||
@@ -2491,12 +2491,12 @@ p {
|
||||
}
|
||||
|
||||
.member-overlay-body .member-card .member-header {
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 20px 24px 0px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.member-overlay-body .member-card .member-details {
|
||||
padding: 20px 24px;
|
||||
padding: 0px 24px;
|
||||
}
|
||||
|
||||
/* Hide expand icon in overlay since card is always expanded */
|
||||
@@ -2509,6 +2509,91 @@ p {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Disable hover effects on topology dialog member cards */
|
||||
.member-overlay-body .member-card:hover {
|
||||
transform: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.member-overlay-body .member-card::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.member-overlay-body .member-card .member-header:hover {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* Label chips styling for overlay */
|
||||
.member-overlay-body .member-labels {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* unified with .label-chip */
|
||||
.member-overlay-body .label-chip {
|
||||
margin-right: 10px;
|
||||
background: rgba(30, 58, 138, 0.35);
|
||||
color: #dbeafe;
|
||||
padding: 0.25rem 0.6rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
border: 1px solid rgba(59, 130, 246, 0.4);
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
/* Labels section styling in node details */
|
||||
.detail-section {
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.detail-section-title {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
/* Resource and API chips styling */
|
||||
.member-resources, .member-api {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.resources-title, .api-title {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.resources-container, .api-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.resource-chip, .api-chip {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
color: #60a5fa;
|
||||
padding: 4px 12px;
|
||||
border-radius: 16px;
|
||||
font-size: 0.8rem;
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
font-family: 'Courier New', monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.api-chip {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: #10b981;
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
/* Highlight animation for member cards */
|
||||
.member-card.highlighted {
|
||||
animation: highlight-pulse 2s ease-in-out;
|
||||
|
||||
Reference in New Issue
Block a user