| class CustomNodeGroup extends HTMLElement { |
| connectedCallback() { |
| const title = this.getAttribute('title') || 'Nodes'; |
| const status = this.getAttribute('status') || 'operational'; |
| const count = this.getAttribute('count') || '0'; |
| |
| this.attachShadow({ mode: 'open' }); |
| this.shadowRoot.innerHTML = ` |
| <style> |
| .card { |
| background: rgba(15, 23, 42, 0.5); |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(30, 41, 59, 0.5); |
| transition: all 0.2s; |
| } |
| |
| .card:hover { |
| border-color: rgba(56, 182, 255, 0.3); |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); |
| } |
| |
| .status-badge { |
| padding: 0.25rem 0.5rem; |
| font-size: 0.75rem; |
| border-radius: 9999px; |
| } |
| |
| .status-operational { |
| background-color: rgba(74, 222, 128, 0.1); |
| color: rgb(74, 222, 128); |
| } |
| |
| .status-degraded { |
| background-color: rgba(250, 204, 21, 0.1); |
| color: rgb(250, 204, 21); |
| } |
| |
| .status-outage { |
| background-color: rgba(248, 113, 113, 0.1); |
| color: rgb(248, 113, 113); |
| } |
| |
| .node-item { |
| border-bottom: 1px solid rgba(30, 41, 59, 0.5); |
| transition: background-color 0.2s; |
| } |
| |
| .node-item:last-child { |
| border-bottom: none; |
| } |
| |
| .node-item:hover { |
| background-color: rgba(30, 41, 59, 0.3); |
| } |
| |
| .latency-normal { |
| color: rgb(74, 222, 128); |
| } |
| |
| .latency-warning { |
| color: rgb(250, 204, 21); |
| } |
| |
| .latency-high { |
| color: rgb(248, 113, 113); |
| } |
| </style> |
| <div class="card rounded-xl overflow-hidden mb-6"> |
| <div class="px-6 py-4 flex justify-between items-center border-b border-graphite-800"> |
| <h3 class="text-lg font-semibold">${title}</h3> |
| <span class="status-badge status-${status} flex items-center"> |
| <i data-feather="${status === 'operational' ? 'check-circle' : status === 'degraded' ? 'alert-triangle' : 'x-circle'}" class="w-3 h-3 mr-1"></i> |
| ${status.charAt(0).toUpperCase() + status.slice(1)} (${count}) |
| </span> |
| </div> |
| |
| <div class="divide-y divide-graphite-800"> |
| ${this.generateNodeItems(title, count)} |
| </div> |
| </div> |
| `; |
| } |
| |
| generateNodeItems(group, count) { |
| const nodes = []; |
| const groupPrefix = group.split(' ')[0].toLowerCase(); |
| |
| for (let i = 1; i <= parseInt(count); i++) { |
| const latency = Math.floor(Math.random() * 500) + 50; |
| const latencyClass = latency <= 300 ? 'latency-normal' : |
| latency <= 700 ? 'latency-warning' : 'latency-high'; |
| |
| nodes.push(` |
| <div class="node-item px-6 py-4 flex justify-between items-center"> |
| <div class="flex items-center"> |
| <i data-feather="server" class="w-5 h-5 mr-3 text-blue-400"></i> |
| <div> |
| <div class="font-medium">${groupPrefix}-node-${i}</div> |
| <div class="text-xs text-graphite-400">${groupPrefix.toUpperCase()} • us-east-1</div> |
| </div> |
| </div> |
| <div class="${latencyClass} font-mono">${latency} ms</div> |
| </div> |
| `); |
| } |
| |
| return nodes.join(''); |
| } |
| } |
|
|
| customElements.define('custom-node-group', CustomNodeGroup); |