mio dashboard
docs.kosmonum.space/mio/
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>MIO Instances</title>
7
8 <style>
9 * {
10 margin: 0;
11 padding: 0;
12 box-sizing: border-box;
13 }
14
15 body {
16 font-family: system-ui,sans-serif;
17 background: linear-gradient(135deg, #4c566a 0%, #2e3440 100%);
18 min-height: 100vh;
19 padding: 20px;
20 }
21
22 /* Header Styles */
23 .header {
24 background: #3b4252;
25 padding: 16px 24px;
26 border-radius: 8px;
27 display: flex;
28 align-items: center;
29 justify-content: space-between;
30 margin-bottom: 24px;
31 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
32 border-left: 8px solid #88c0d0;
33 }
34
35 .header-left {
36 display: flex;
37 align-items: center;
38 gap: 12px;
39 }
40
41 .header-title {
42 font-size: 18px;
43 font-weight: 600;
44 color: #eceff4;
45 text-transform: uppercase;
46 }
47
48 .butterfly-icon {
49 font-size: 36px;
50 }
51
52 .header-right {
53 display: flex;
54 align-items: center;
55 gap: 12px;
56 margin-left: 12px;
57 }
58
59 .btn {
60 padding: 8px 12px;
61 border: none;
62 background: #4c566a;
63 color: #eceff4;
64 border-radius: 6px;
65 cursor: pointer;
66 font-size: 16px;
67 transition: all 0.2s ease;
68 display: flex;
69 align-items: center;
70 justify-content: center;
71 gap: 6px;
72 }
73
74 .btn:hover {
75 background: #5e81ac;
76 transform: translateY(-1px);
77 }
78
79 .btn-primary {
80 background: #4c566a;
81 font-weight: 500;
82 }
83
84 .btn-icon {
85 width: 36px;
86 height: 36px;
87 padding: 0;
88 display: flex;
89 align-items: center;
90 justify-content: center;
91 border-radius: 6px;
92 }
93
94 .avatar-btn {
95 border-radius: 50%;
96 background: linear-gradient(135deg, #81a1c1 0%, #5e81ac 100%);
97 color: #eceff4;
98 font-weight: 600;
99 position: relative;
100 cursor: pointer;
101 }
102
103 /* Dropdown Menus */
104 .dropdown {
105 position: relative;
106 display: inline-block;
107 }
108
109 .dropdown-menu {
110 position: absolute;
111 top: 100%;
112 right: 0;
113 background: #3b4252;
114 border-radius: 6px;
115 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
116 min-width: 160px;
117 z-index: 1000;
118 display: none;
119 margin-top: 4px;
120 overflow: hidden;
121 border: 1px solid #4c566a;
122 }
123
124 .dropdown-menu.active {
125 display: block;
126 }
127
128 .dropdown-item {
129 padding: 10px 16px;
130 display: flex;
131 align-items: center;
132 gap: 8px;
133 color: #eceff4;
134 cursor: pointer;
135 transition: background 0.2s ease;
136 border: none;
137 background: none;
138 width: 100%;
139 text-align: left;
140 font-size: 14px;
141 }
142
143 .dropdown-item:hover {
144 background: #4c566a;
145 }
146
147 .dropdown-item.danger {
148 color: #bf616a;
149 }
150
151 .dropdown-item.danger:hover {
152 background: #4c566a;
153 }
154
155 /* Instances Container */
156 .instances-container {
157 max-width: 1200px;
158 margin: 0 auto;
159 }
160
161 /* Instance Card */
162 .instance {
163 background: #3b4252;
164 padding: 16px 12px 16px 20px;
165 border-radius: 8px;
166 margin-bottom: 12px;
167 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
168 transition: all 0.2s ease;
169 display: grid;
170 grid-template-columns: auto 1fr auto auto;
171 grid-template-rows: auto auto;
172 gap: 12px 12px;
173 align-items: start;
174 border-left: 8px solid #b48ead;
175 }
176
177 .instance:hover {
178 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
179 background: #434c5e;
180 }
181
182 .instance.healthy {
183 border-left-color: #a3be8c;
184 }
185
186 .instance.warning {
187 border-left-color: #ebcb8b;
188 }
189
190 .instance.critical {
191 border-left-color: #bf616a;
192 }
193
194 @keyframes pulse {
195 0%, 100% { opacity: 1; }
196 50% { opacity: 0.5; }
197 }
198
199 /* First line: name + tags + menu */
200 .instance-name {
201 font-weight: 500;
202 color: #eceff4;
203 display: flex;
204 align-items: center;
205 grid-column: 1;
206 grid-row: 1;
207 font-size: 14px;
208 letter-spacing: 0.5px;
209 height: 24px;
210 }
211
212 /* Tags container */
213 .instance-tags {
214 display: flex;
215 gap: 8px;
216 align-items: center;
217 grid-column: 2;
218 grid-row: 1;
219 justify-self: end;
220 height: 24px;
221 }
222
223 .tag {
224 display: inline-flex;
225 align-items: center;
226 padding: 4px 8px;
227 background: #4c566a;
228 color: #88c0d0;
229 border-radius: 4px;
230 font-size: 11px;
231 font-weight: 500;
232 cursor: help;
233 transition: all 0.2s ease;
234 position: relative;
235 text-transform: uppercase;
236 border: 1px solid #5e81ac;
237 }
238
239 .tag:hover {
240 background: #5e81ac;
241 color: #eceff4;
242 }
243
244 /* Tag tooltip */
245 .tag::before {
246 content: attr(data-label);
247 position: absolute;
248 bottom: -24px;
249 left: 50%;
250 transform: translateX(-50%);
251 background: #2e3440;
252 color: #88c0d0;
253 padding: 4px 8px;
254 border-radius: 4px;
255 font-size: 10px;
256 font-weight: 600;
257 white-space: nowrap;
258 opacity: 0;
259 pointer-events: none;
260 transition: opacity 0.2s ease;
261 z-index: 100;
262 border: 1px solid #4c566a;
263 }
264
265 .tag:hover::before {
266 opacity: 1;
267 }
268
269 /* Metrics on second line */
270 .metrics {
271 display: flex;
272 gap: 24px;
273 grid-column: 1 / 3;
274 grid-row: 2;
275 }
276
277 .metric {
278 display: flex;
279 flex-direction: column;
280 gap: 4px;
281 flex: 1;
282 position: relative;
283 }
284
285 .metric-bar {
286 height: 8px;
287 background: #2e3440;
288 border-radius: 4px;
289 overflow: hidden;
290 cursor: pointer;
291 position: relative;
292 }
293
294 .metric-fill {
295 height: 100%;
296 border-radius: 4px;
297 transition: width 0.3s ease;
298 }
299
300 .metric-utilization .metric-fill {
301 background: linear-gradient(90deg, #81a1c1 0%, #5e81ac 100%);
302 }
303
304 .metric-saturation .metric-fill {
305 background: linear-gradient(90deg, #d08770 0%, #bf616a 100%);
306 }
307
308 .metric-error .metric-fill {
309 background: linear-gradient(90deg, #bf616a 0%, #a3414a 100%);
310 }
311
312 .metric-latency .metric-fill {
313 background: linear-gradient(90deg, #b48ead 0%, #a3429e 100%);
314 }
315
316 .metric-uptime .metric-fill {
317 background: linear-gradient(90deg, #a3be8c 0%, #8fb78a 100%);
318 }
319
320 /* Metric value tooltip */
321 .metric-tooltip {
322 position: absolute;
323 bottom: -24px;
324 left: 50%;
325 transform: translateX(-50%);
326 background: #2e3440;
327 color: #88c0d0;
328 padding: 4px 8px;
329 border-radius: 4px;
330 font-size: 11px;
331 font-weight: 600;
332 white-space: nowrap;
333 opacity: 0;
334 pointer-events: none;
335 transition: opacity 0.2s ease;
336 z-index: 100;
337 border: 1px solid #4c566a;
338 }
339
340 .metric-bar:hover + .metric-tooltip,
341 .metric-tooltip:hover {
342 opacity: 1;
343 }
344
345 /* Instance Menu */
346 .instance-menu {
347 flex-shrink: 0;
348 position: relative;
349 grid-column: 3;
350 grid-row: 1;
351 justify-self: end;
352 display: flex;
353 align-items: center;
354 height: 24px;
355 }
356
357 .menu-btn {
358 background: none;
359 border: 1px solid #4c566a;
360 font-size: 18px;
361 color: #81a1c1;
362 cursor: pointer;
363 padding: 4px 8px;
364 border-radius: 4px;
365 transition: all 0.2s ease;
366 display: flex;
367 align-items: center;
368 justify-content: center;
369 height: 24px;
370 }
371
372 .menu-btn:hover {
373 background: #4c566a;
374 color: #88c0d0;
375 }
376
377 /* Responsive Design */
378 @media (max-width: 768px) {
379 .instance {
380 grid-template-columns: auto 1fr auto;
381 }
382
383 .instance-name {
384 font-size: 13px;
385 }
386
387 .instance-tags {
388 }
389
390 .metrics {
391 gap: 12px;
392 }
393
394 .metric-tooltip {
395 bottom: -28px;
396 }
397 }
398
399 @media (max-width: 480px) {
400 .header {
401 padding-left: 16px;
402 padding-right: 16px;
403 }
404
405 .header-right {
406 }
407
408 .header-title {
409 font-size: 16px;
410 }
411
412 .instance {
413 grid-template-columns: auto 1fr auto;
414 gap: 8px;
415 padding-left: 16px;
416 padding-right: 16px;
417 }
418
419 .instance-tags {
420 gap: 4px;
421 }
422
423 .metrics {
424 gap: 8px;
425 }
426
427 .tag {
428 font-size: 8px;
429 padding: 2px 4px;
430 }
431 }
432 </style>
433</head>
434<body>
435 <!-- Header -->
436 <div class="header">
437 <div class="header-left">
438 <span class="butterfly-icon">🦋</span>
439 <h1 class="header-title">MIO Instances</h1>
440 </div>
441 <div class="header-right">
442 <button class="btn btn-primary btn-icon" onclick="showAddInstanceModal()" title="Add Instance">
443 ➕
444 </button>
445 <div class="dropdown">
446 <button class="avatar-btn btn" onclick="toggleProfileMenu()">C</button>
447 <div class="dropdown-menu" id="profileMenu">
448 <button class="dropdown-item">👤 Identity</button>
449 <button class="dropdown-item">🔑 Secrets</button>
450 <button class="dropdown-item danger">🚪 Logout</button>
451 </div>
452 </div>
453 </div>
454 </div>
455
456 <!-- Instances List -->
457 <div class="instances-container" id="instancesList">
458 <!-- Instance 1 -->
459 <div class="instance critical">
460 <div class="instance-name">netti</div>
461 <div class="instance-tags">
462 <span class="tag" data-label="App">netbox</span>
463 <span class="tag" data-label="Location">us-east-1</span>
464 </div>
465 <div class="instance-menu dropdown">
466 <button class="menu-btn" onclick="toggleInstanceMenu(this)">☰</button>
467 <div class="dropdown-menu">
468 <button class="dropdown-item">💻 Terminal</button>
469 <button class="dropdown-item">💾 Backup</button>
470 <button class="dropdown-item">⬆️ Upgrade</button>
471 <button class="dropdown-item">🔄 Reboot</button>
472 <button class="dropdown-item danger">🗑️ Delete</button>
473 </div>
474 </div>
475 <div class="metrics">
476 <div class="metric metric-utilization">
477 <div class="metric-bar">
478 <div class="metric-fill" style="width: 15%"></div>
479 </div>
480 <div class="metric-tooltip">Utilization: 15%</div>
481 </div>
482 <div class="metric metric-saturation">
483 <div class="metric-bar">
484 <div class="metric-fill" style="width: 32%"></div>
485 </div>
486 <div class="metric-tooltip">Saturation: 32%</div>
487 </div>
488 <div class="metric metric-error">
489 <div class="metric-bar">
490 <div class="metric-fill" style="width: 62%"></div>
491 </div>
492 <div class="metric-tooltip">Error Rate: 62%</div>
493 </div>
494 <div class="metric metric-latency">
495 <div class="metric-bar">
496 <div class="metric-fill" style="width: 45%"></div>
497 </div>
498 <div class="metric-tooltip">Latency: 120ms</div>
499 </div>
500 <div class="metric metric-uptime">
501 <div class="metric-bar">
502 <div class="metric-fill" style="width: 20.9%"></div>
503 </div>
504 <div class="metric-tooltip">Uptime: 20.9%</div>
505 </div>
506 </div>
507 </div>
508
509 <!-- Instance 2 -->
510 <div class="instance warning">
511 <div class="instance-name">papi</div>
512 <div class="instance-tags">
513 <span class="tag" data-label="App">paperless</span>
514 <span class="tag" data-label="Location">eu-west-1</span>
515 </div>
516 <div class="instance-menu dropdown">
517 <button class="menu-btn" onclick="toggleInstanceMenu(this)">☰</button>
518 <div class="dropdown-menu">
519 <button class="dropdown-item">💻 Terminal</button>
520 <button class="dropdown-item">💾 Backup</button>
521 <button class="dropdown-item">⬆️ Upgrade</button>
522 <button class="dropdown-item">🔄 Reboot</button>
523 <button class="dropdown-item danger">🗑️ Delete</button>
524 </div>
525 </div>
526 <div class="metrics">
527 <div class="metric metric-utilization">
528 <div class="metric-bar">
529 <div class="metric-fill" style="width: 78%"></div>
530 </div>
531 <div class="metric-tooltip">Utilization: 78%</div>
532 </div>
533 <div class="metric metric-saturation">
534 <div class="metric-bar">
535 <div class="metric-fill" style="width: 62%"></div>
536 </div>
537 <div class="metric-tooltip">Saturation: 62%</div>
538 </div>
539 <div class="metric metric-error">
540 <div class="metric-bar">
541 <div class="metric-fill" style="width: 5%"></div>
542 </div>
543 <div class="metric-tooltip">Error Rate: 0.5%</div>
544 </div>
545 <div class="metric metric-latency">
546 <div class="metric-bar">
547 <div class="metric-fill" style="width: 72%"></div>
548 </div>
549 <div class="metric-tooltip">Latency: 245ms</div>
550 </div>
551 <div class="metric metric-uptime">
552 <div class="metric-bar">
553 <div class="metric-fill" style="width: 98.5%"></div>
554 </div>
555 <div class="metric-tooltip">Uptime: 98.5%</div>
556 </div>
557 </div>
558 </div>
559
560 <!-- Instance 3 -->
561 <div class="instance">
562 <div class="instance-name">ci</div>
563 <div class="instance-tags">
564 <span class="tag" data-label="App">buildbot</span>
565 <span class="tag" data-label="Location">ap-south-1</span>
566 </div>
567 <div class="instance-menu dropdown">
568 <button class="menu-btn" onclick="toggleInstanceMenu(this)">☰</button>
569 <div class="dropdown-menu">
570 <button class="dropdown-item">💻 Terminal</button>
571 <button class="dropdown-item">💾 Backup</button>
572 <button class="dropdown-item">⬆️ Upgrade</button>
573 <button class="dropdown-item">🔄 Reboot</button>
574 <button class="dropdown-item danger">🗑️ Delete</button>
575 </div>
576 </div>
577 <div class="metrics">
578 <div class="metric metric-utilization">
579 <div class="metric-bar">
580 <div class="metric-fill" style="width: 92%"></div>
581 </div>
582 <div class="metric-tooltip">Utilization: 92%</div>
583 </div>
584 <div class="metric metric-saturation">
585 <div class="metric-bar">
586 <div class="metric-fill" style="width: 88%"></div>
587 </div>
588 <div class="metric-tooltip">Saturation: 88%</div>
589 </div>
590 <div class="metric metric-error">
591 <div class="metric-bar">
592 <div class="metric-fill" style="width: 18%"></div>
593 </div>
594 <div class="metric-tooltip">Error Rate: 1.8%</div>
595 </div>
596 <div class="metric metric-latency">
597 <div class="metric-bar">
598 <div class="metric-fill" style="width: 95%"></div>
599 </div>
600 <div class="metric-tooltip">Latency: 890ms</div>
601 </div>
602 <div class="metric metric-uptime">
603 <div class="metric-bar">
604 <div class="metric-fill" style="width: 85.2%"></div>
605 </div>
606 <div class="metric-tooltip">Uptime: 85.2%</div>
607 </div>
608 </div>
609 </div>
610
611 <!-- Instance 4 -->
612 <div class="instance healthy">
613 <div class="instance-name">chat</div>
614 <div class="instance-tags">
615 <span class="tag" data-label="App">irc</span>
616 <span class="tag" data-label="App">xmpp</span>
617 <span class="tag" data-label="Location">us-west-2</span>
618 </div>
619 <div class="instance-menu dropdown">
620 <button class="menu-btn" onclick="toggleInstanceMenu(this)">☰</button>
621 <div class="dropdown-menu">
622 <button class="dropdown-item">💻 Terminal</button>
623 <button class="dropdown-item">💾 Backup</button>
624 <button class="dropdown-item">⬆️ Upgrade</button>
625 <button class="dropdown-item">🔄 Reboot</button>
626 <button class="dropdown-item danger">🗑️ Delete</button>
627 </div>
628 </div>
629 <div class="metrics">
630 <div class="metric metric-utilization">
631 <div class="metric-bar">
632 <div class="metric-fill" style="width: 54%"></div>
633 </div>
634 <div class="metric-tooltip">Utilization: 54%</div>
635 </div>
636 <div class="metric metric-saturation">
637 <div class="metric-bar">
638 <div class="metric-fill" style="width: 28%"></div>
639 </div>
640 <div class="metric-tooltip">Saturation: 28%</div>
641 </div>
642 <div class="metric metric-error">
643 <div class="metric-bar">
644 <div class="metric-fill" style="width: 1%"></div>
645 </div>
646 <div class="metric-tooltip">Error Rate: 0.1%</div>
647 </div>
648 <div class="metric metric-latency">
649 <div class="metric-bar">
650 <div class="metric-fill" style="width: 35%"></div>
651 </div>
652 <div class="metric-tooltip">Latency: 85ms</div>
653 </div>
654 <div class="metric metric-uptime">
655 <div class="metric-bar">
656 <div class="metric-fill" style="width: 99.98%"></div>
657 </div>
658 <div class="metric-tooltip">Uptime: 99.98%</div>
659 </div>
660 </div>
661 </div>
662 </div>
663
664 <script>
665 // Close all dropdowns when clicking outside
666 document.addEventListener('click', function(event) {
667 if (!event.target.closest('.dropdown')) {
668 document.querySelectorAll('.dropdown-menu').forEach(menu => {
669 menu.classList.remove('active');
670 });
671 }
672 });
673
674 // Toggle profile menu
675 function toggleProfileMenu() {
676 const menu = document.getElementById('profileMenu');
677 menu.classList.toggle('active');
678 }
679
680 // Toggle instance menu
681 function toggleInstanceMenu(button) {
682 const menu = button.nextElementSibling;
683
684 // Close all other menus
685 document.querySelectorAll('.instance-menu .dropdown-menu').forEach(m => {
686 if (m !== menu) m.classList.remove('active');
687 });
688
689 menu.classList.toggle('active');
690 }
691
692 // Add instance modal
693 function showAddInstanceModal() {
694 alert('Add new instance modal would open here');
695 }
696
697 // Handle instance menu actions
698 document.querySelectorAll('.instance-menu .dropdown-item').forEach(item => {
699 item.addEventListener('click', function() {
700 const action = this.textContent.trim();
701 console.log('Instance action:', action);
702 });
703 });
704
705 // Handle profile menu actions
706 document.querySelectorAll('#profileMenu .dropdown-item').forEach(item => {
707 item.addEventListener('click', function() {
708 const action = this.textContent.trim();
709 console.log('Profile action:', action);
710 });
711 });
712 </script>
713</body>
714</html>