mirror of
https://github.com/myronblair/web-dashboard
synced 2026-06-30 09:41:28 -05:00
732 lines
32 KiB
HTML
732 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Myron's Dashboard</title>
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #0f1117;
|
||
--surface: #1a1d27;
|
||
--surface2: #22263a;
|
||
--border: #2e3350;
|
||
--accent: #4f8ef7;
|
||
--accent2: #7c5af7;
|
||
--green: #22c55e;
|
||
--yellow: #eab308;
|
||
--red: #ef4444;
|
||
--orange: #f97316;
|
||
--pink: #ec4899;
|
||
--cyan: #06b6d4;
|
||
--text: #e2e4f0;
|
||
--muted: #8b90a8;
|
||
--radius: 12px;
|
||
}
|
||
|
||
body {
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||
min-height: 100vh;
|
||
padding: 2rem 1.5rem;
|
||
}
|
||
|
||
header {
|
||
text-align: center;
|
||
margin-bottom: 2.5rem;
|
||
}
|
||
header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
header p {
|
||
color: var(--muted);
|
||
font-size: 0.85rem;
|
||
margin-top: 0.35rem;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 1.25rem;
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
transition: border-color 0.2s, transform 0.2s;
|
||
}
|
||
.card:hover { border-color: var(--accent); transform: translateY(-2px); }
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
padding: 0.85rem 1rem;
|
||
background: var(--surface2);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.card-header .icon {
|
||
font-size: 1.1rem;
|
||
width: 28px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 6px;
|
||
flex-shrink: 0;
|
||
}
|
||
.card-header h2 {
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.links { padding: 0.5rem 0; }
|
||
|
||
.link-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.6rem 1rem;
|
||
text-decoration: none;
|
||
color: var(--text);
|
||
font-size: 0.9rem;
|
||
transition: background 0.15s;
|
||
border-radius: 0;
|
||
}
|
||
.link-item:hover { background: var(--surface2); }
|
||
.link-item .dot {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
flex-shrink: 0;
|
||
}
|
||
.link-item .name { flex: 1; font-weight: 500; }
|
||
.link-item .sub {
|
||
font-size: 0.75rem;
|
||
color: var(--muted);
|
||
}
|
||
.link-item .arrow {
|
||
color: var(--muted);
|
||
font-size: 0.7rem;
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.link-item:hover .arrow { opacity: 1; }
|
||
|
||
.dot-blue { background: var(--accent); }
|
||
.dot-purple { background: var(--accent2); }
|
||
.dot-green { background: var(--green); }
|
||
.dot-yellow { background: var(--yellow); }
|
||
.dot-red { background: var(--red); }
|
||
.dot-orange { background: var(--orange); }
|
||
.dot-pink { background: var(--pink); }
|
||
.dot-cyan { background: var(--cyan); }
|
||
.dot-gray { background: var(--muted); }
|
||
|
||
.icon-blue { background: rgba(79,142,247,0.15); }
|
||
.icon-purple { background: rgba(124,90,247,0.15); }
|
||
.icon-green { background: rgba(34,197,94,0.15); }
|
||
.icon-yellow { background: rgba(234,179,8,0.15); }
|
||
.icon-orange { background: rgba(249,115,22,0.15); }
|
||
.icon-pink { background: rgba(236,72,153,0.15); }
|
||
.icon-cyan { background: rgba(6,182,212,0.15); }
|
||
.icon-red { background: rgba(239,68,68,0.15); }
|
||
|
||
.divider {
|
||
height: 1px;
|
||
background: var(--border);
|
||
margin: 0.25rem 1rem;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
body { padding: 1rem; }
|
||
.grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<h1>⚡ Blair HQ</h1>
|
||
<p id="clock"></p>
|
||
</header>
|
||
|
||
<div class="grid">
|
||
|
||
<!-- INFRASTRUCTURE & JARVIS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-blue">🖥️</div>
|
||
<h2>Infrastructure</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://jarvis.orbishosting.com/login.php" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">JARVIS Dashboard</span><span class="sub">Main UI</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://jarvis.orbishosting.com/admin" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">JARVIS Admin</span><span class="sub">Admin panel</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://10.48.200.90:8006" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Proxmox PVE1</span><span class="sub">Primary hypervisor</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://10.48.200.91:8006" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Proxmox PVE2</span><span class="sub">Secondary hypervisor</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.200:81" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Nginx Proxy Manager</span><span class="sub">10.48.200.200</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="http://10.48.200.210:11434" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Ollama</span><span class="sub">Local LLM</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.249:5000" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Synology NAS</span><span class="sub">10.48.200.249</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://gitea.orbishosting.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Gitea</span><span class="sub">Local Git server</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.211/phpmyadmin" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">phpMyAdmin</span><span class="sub">JARVIS DB</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DOWNLOADS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-green">📄</div>
|
||
<h2>Downloads</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="/downloads/INFRASTRUCTURE-REFERENCE.md" download="INFRASTRUCTURE-REFERENCE.md">
|
||
<span class="dot dot-green"></span><span class="name">Infrastructure Reference</span><span class="sub">Complete system map & credentials</span><span class="arrow">↓</span>
|
||
</a>
|
||
<a class="link-item" href="/downloads/msp360-linux-installer.zip" download="msp360-linux-installer.zip">
|
||
<span class="dot dot-cyan"></span><span class="name">MSP360 Linux Installer</span><span class="sub">Backup setup scripts + Gitea config fetch</span><span class="arrow">↓</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HOME AUTOMATION -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-green">🏠</div>
|
||
<h2>Home Automation</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://hoa.orbishosting.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Home Assistant</span><span class="sub">hoa.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.18:8581" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">HomeBridge</span><span class="sub">10.48.200.18</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.97:8123" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">HA (Direct LAN)</span><span class="sub">10.48.200.97</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MEDIA -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-purple">🎬</div>
|
||
<h2>Media</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="http://10.48.200.33:8096" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Jellyfin</span><span class="sub">Media server</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:8989" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Sonarr</span><span class="sub">TV shows</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:7878" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">Radarr</span><span class="sub">Movies</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:9696" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Prowlarr</span><span class="sub">Indexers</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.35:8080" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">qBittorrent</span><span class="sub">Downloads</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- HOSTING PANEL -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-cyan">⚙️</div>
|
||
<h2>NovaCPX Panel</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://admin.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-red"></span><span class="name">Admin Panel</span><span class="sub">Root access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://reseller.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Reseller Panel</span><span class="sub">Reseller access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://panel.novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Client Panel</span><span class="sub">End user access</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://novacpx.orbishosting.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Client Panel (alt)</span><span class="sub">novacpx.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- WEBSITES -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-pink">🌐</div>
|
||
<h2>Websites</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://orbishosting.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Orbis Hosting</span><span class="sub">orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://orbis.orbishosting.com" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Orbis Portal</span><span class="sub">orbis.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://tomsjavajive.com" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">Tom's Java Jive</span><span class="sub">tomsjavajive.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://tomtomgames.com" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Tom Tom Games</span><span class="sub">tomtomgames.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://epictravelexpeditions.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Epic Travel</span><span class="sub">epictravelexpeditions.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://parkerslingshotrentals.com" target="_blank">
|
||
<span class="dot dot-red"></span><span class="name">Parker Slingshot Rentals</span><span class="sub">parkerslingshotrentals.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- COMMUNICATIONS -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-orange">📞</div>
|
||
<h2>Communications</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://fusion.orbishosting.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">FusionPBX</span><span class="sub">fusion.orbishosting.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://fusion.orbishosting.com/app/settings" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">PBX Settings</span><span class="sub">Extensions & trunks</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- NETWORKING -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-yellow">🔒</div>
|
||
<h2>Networking</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://10.48.200.1:9443" target="_blank">
|
||
<span class="dot dot-yellow"></span><span class="name">FortiGate</span><span class="sub">10.48.200.1:9443</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.80" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">WB610H Bridge</span><span class="sub">10.48.200.80</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="http://10.48.200.250" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Goalake Switch 1</span><span class="sub">10.48.200.250</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.251" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Goalake Switch 2</span><span class="sub">10.48.200.251</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://10.48.200.67" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">WireGuard CT</span><span class="sub">10.48.200.67</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://login.tailscale.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Tailscale</span><span class="sub">login.tailscale.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://dash.cloudflare.com" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Cloudflare</span><span class="sub">DNS & CDN</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- GITHUB -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-purple">🐙</div>
|
||
<h2>GitHub Repos</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://github.com/myronblair/jarvis" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">JARVIS</span><span class="sub">myronblair/jarvis</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/novacpx" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">NovaCPX</span><span class="sub">myronblair/novacpx</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/web-dashboard" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Web Dashboard</span><span class="sub">myronblair/web-dashboard</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://github.com/myronblair/tomsjavajive" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Tom's Java Jive</span><span class="sub">myronblair/tomsjavajive</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/tomtomgames" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">TomTom Games</span><span class="sub">myronblair/tomtomgames</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/epictravelexpeditions" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">Epic Travel Expeditions</span><span class="sub">myronblair/epictravelexpeditions</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/parkerslingshot" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Parker Slingshot</span><span class="sub">myronblair/parkerslingshot</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/parkerslingshotrentals" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Parker Slingshot Rentals</span><span class="sub">myronblair/parkerslingshotrentals</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/orbishosting" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Orbis Hosting</span><span class="sub">myronblair/orbishosting</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/orbis-hosting-portal" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Orbis Hosting Portal</span><span class="sub">myronblair/orbis-hosting-portal</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://github.com/myronblair/infra" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Infra</span><span class="sub">myronblair/infra</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/mediastack" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">MediaStack</span><span class="sub">myronblair/mediastack</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/do-server-config" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">DO Server Config</span><span class="sub">myronblair/do-server-config</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/proxmox-config" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Proxmox Config</span><span class="sub">myronblair/proxmox-config</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/myronblair/fusionpbx-config" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">FusionPBX Config</span><span class="sub">myronblair/fusionpbx-config</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://github.com/myronblair" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">All Repos</span><span class="sub">github.com/myronblair</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DO SERVER -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-blue">🌊</div>
|
||
<h2>DigitalOcean</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://cloud.digitalocean.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">DO Dashboard</span><span class="sub">cloud.digitalocean.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="http://165.22.1.228:8090" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">CyberPanel</span><span class="sub">165.22.1.228:8090</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- VANGUARD TECHNOLOGY -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<div class="icon icon-cyan">🛡️</div>
|
||
<h2>Vanguard Technology</h2>
|
||
</div>
|
||
<div class="links">
|
||
<a class="link-item" href="https://portal.office.com" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">365 Portal</span><span class="sub">portal.office.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://docs.vanguardtc.com" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">Docs Dashboard</span><span class="sub">docs.vanguardtc.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://vanguardtc.com/downloads/" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Vanguard Downloads</span><span class="sub">vanguardtc.com/downloads</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://vanguardtc.shield.syncromsp.com" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Syncro RMM</span><span class="sub">vanguardtc.shield.syncromsp.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://connect.vanguardtc.com/Host#Support/All Sessions//cde1ee04-d49b-47b2-a687-36cb6964761f/Start" target="_blank">
|
||
<span class="dot dot-green"></span><span class="name">Connect Remote</span><span class="sub">Host — Support sessions</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://connect.vanguardtc.com" target="_blank">
|
||
<span class="dot dot-cyan"></span><span class="name">Connect Join</span><span class="sub">connect.vanguardtc.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://msp.eset.com/ema/dashboard" target="_blank">
|
||
<span class="dot dot-blue"></span><span class="name">ESET Admin</span><span class="sub">msp.eset.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://app.scalepad.com/account/home" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">ScalePad</span><span class="sub">app.scalepad.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://my.hivelocity.net/auth/login" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">Hivelocity</span><span class="sub">my.hivelocity.net</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://usa.ingrammicro.com/cep/app/my/dashboard" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">Ingram Micro</span><span class="sub">usa.ingrammicro.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://partnerfirst.us.tdsynnex.com/login-portal/ec" target="_blank">
|
||
<span class="dot dot-gray"></span><span class="name">TD SYNNEX</span><span class="sub">partnerfirst.us.tdsynnex.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<div class="divider"></div>
|
||
<a class="link-item" href="https://manage.flowroute.com/accounts/profile/select" target="_blank">
|
||
<span class="dot dot-purple"></span><span class="name">FlowRoute</span><span class="sub">manage.flowroute.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
<a class="link-item" href="https://docs.fusionpbx.com/en/latest/#" target="_blank">
|
||
<span class="dot dot-orange"></span><span class="name">FusionPBX Docs</span><span class="sub">docs.fusionpbx.com</span><span class="arrow">↗</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
function tick() {
|
||
const now = new Date();
|
||
document.getElementById('clock').textContent =
|
||
now.toLocaleDateString('en-US', {weekday:'long',year:'numeric',month:'long',day:'numeric'}) +
|
||
' · ' +
|
||
now.toLocaleTimeString('en-US', {hour:'2-digit',minute:'2-digit',second:'2-digit'});
|
||
}
|
||
tick(); setInterval(tick, 1000);
|
||
|
||
// ── Notes — server-backed so they sync across all devices ─────────────
|
||
const API = '/notes.php';
|
||
let _notes = [];
|
||
|
||
function escHtml(s) {
|
||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');
|
||
}
|
||
|
||
async function notesApi(action, body = {}) {
|
||
try {
|
||
const r = await fetch(API, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ action, ...body }),
|
||
credentials: 'same-origin',
|
||
});
|
||
return await r.json();
|
||
} catch (e) { return { ok: false }; }
|
||
}
|
||
|
||
async function fetchNotes() {
|
||
const el = document.getElementById('notes-list');
|
||
if (el && !_notes.length) el.innerHTML = '<div style="color:#8b90a8;font-size:.85rem">Loading…</div>';
|
||
try {
|
||
const r = await fetch(API + '?action=list', { credentials: 'same-origin' });
|
||
const d = await r.json();
|
||
_notes = d.notes || [];
|
||
} catch (e) { _notes = []; }
|
||
renderNotes();
|
||
}
|
||
|
||
function renderNotes() {
|
||
const list = document.getElementById('notes-list');
|
||
if (!list) return;
|
||
if (!_notes.length) {
|
||
list.innerHTML = '<div style="color:#8b90a8;font-size:.85rem;padding:.5rem 0">No notes yet. Add one above.</div>';
|
||
return;
|
||
}
|
||
list.innerHTML = _notes.map(n => `
|
||
<div class="note-item ${n.done ? 'note-done' : ''}">
|
||
<button class="note-check" onclick="toggleNote('${n.id}')" title="${n.done ? 'Mark incomplete' : 'Mark done'}">
|
||
${n.done ? '✅' : '<span class="note-circle"></span>'}
|
||
</button>
|
||
<div class="note-body">
|
||
<div class="note-text">${escHtml(n.text)}</div>
|
||
${n.detail ? `<div class="note-detail">${escHtml(n.detail)}</div>` : ''}
|
||
<div class="note-meta">${new Date(n.ts).toLocaleString('en-US',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'})}</div>
|
||
</div>
|
||
<button class="note-del" onclick="deleteNote('${n.id}')" title="Delete">✕</button>
|
||
</div>`).join('');
|
||
}
|
||
|
||
async function addNote() {
|
||
const txt = document.getElementById('note-input').value.trim();
|
||
const det = document.getElementById('note-detail').value.trim();
|
||
if (!txt) { document.getElementById('note-input').focus(); return; }
|
||
const btn = document.querySelector('.btn-note-add');
|
||
btn.disabled = true;
|
||
await notesApi('add', { text: txt, detail: det });
|
||
btn.disabled = false;
|
||
document.getElementById('note-input').value = '';
|
||
document.getElementById('note-detail').value = '';
|
||
document.getElementById('note-detail').style.display = 'none';
|
||
document.getElementById('note-detail-toggle').textContent = '+ Add details';
|
||
await fetchNotes();
|
||
}
|
||
|
||
async function toggleNote(id) {
|
||
_notes = _notes.map(n => n.id === id ? {...n, done: !n.done} : n);
|
||
renderNotes();
|
||
notesApi('toggle', { id }).then(fetchNotes);
|
||
}
|
||
|
||
async function deleteNote(id) {
|
||
_notes = _notes.filter(n => n.id !== id);
|
||
renderNotes();
|
||
notesApi('delete', { id }).then(fetchNotes);
|
||
}
|
||
|
||
async function clearDone() {
|
||
_notes = _notes.filter(n => !n.done);
|
||
renderNotes();
|
||
notesApi('clear-done').then(fetchNotes);
|
||
}
|
||
|
||
function toggleDetail() {
|
||
const d = document.getElementById('note-detail');
|
||
const btn = document.getElementById('note-detail-toggle');
|
||
const show = d.style.display === 'none';
|
||
d.style.display = show ? 'block' : 'none';
|
||
btn.textContent = show ? '− Hide details' : '+ Add details';
|
||
if (show) d.focus();
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
fetchNotes();
|
||
// Re-sync when tab becomes visible (switching back from another device)
|
||
document.addEventListener('visibilitychange', () => {
|
||
if (document.visibilityState === 'visible') fetchNotes();
|
||
});
|
||
document.getElementById('note-input').addEventListener('keydown', e => {
|
||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); addNote(); }
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- ── NOTES SECTION ─────────────────────────────────────────────────────── -->
|
||
<style>
|
||
.notes-wrap {
|
||
max-width:1400px; margin:2rem auto 0; padding:0 1.5rem 3rem;
|
||
}
|
||
.notes-header {
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
margin-bottom:1.25rem;
|
||
}
|
||
.notes-title {
|
||
font-size:.75rem; font-weight:700; text-transform:uppercase;
|
||
letter-spacing:1.5px; color:#8b90a8;
|
||
}
|
||
.notes-add {
|
||
background:var(--surface); border:1px solid var(--border);
|
||
border-radius:var(--radius); padding:1rem; margin-bottom:1rem;
|
||
}
|
||
.notes-add-row {
|
||
display:flex; gap:.6rem; align-items:center;
|
||
}
|
||
#note-input {
|
||
flex:1; background:var(--surface2); border:1px solid var(--border);
|
||
border-radius:6px; color:var(--text); padding:.6rem .85rem;
|
||
font-size:.9rem; font-family:inherit; outline:none;
|
||
transition:border-color .15s;
|
||
}
|
||
#note-input:focus { border-color:var(--accent); }
|
||
#note-detail {
|
||
width:100%; margin-top:.6rem; background:var(--surface2);
|
||
border:1px solid var(--border); border-radius:6px; color:var(--text);
|
||
padding:.6rem .85rem; font-size:.85rem; font-family:inherit;
|
||
outline:none; resize:vertical; min-height:70px;
|
||
transition:border-color .15s; display:none;
|
||
}
|
||
#note-detail:focus { border-color:var(--accent); }
|
||
.btn-note-add {
|
||
background:var(--accent); color:#fff; border:none; border-radius:6px;
|
||
padding:.6rem 1.1rem; font-size:.85rem; cursor:pointer; font-weight:600;
|
||
white-space:nowrap; transition:opacity .15s;
|
||
}
|
||
.btn-note-add:hover { opacity:.85; }
|
||
#note-detail-toggle {
|
||
background:none; border:none; color:#8b90a8; font-size:.78rem;
|
||
cursor:pointer; padding:.25rem 0; margin-top:.4rem;
|
||
transition:color .15s;
|
||
}
|
||
#note-detail-toggle:hover { color:var(--text); }
|
||
.note-item {
|
||
display:flex; align-items:flex-start; gap:.75rem;
|
||
padding:.8rem .75rem; border-radius:8px;
|
||
background:var(--surface); border:1px solid var(--border);
|
||
margin-bottom:.5rem; transition:opacity .2s, border-color .2s;
|
||
}
|
||
.note-item:hover { border-color:var(--accent); }
|
||
.note-done { opacity:.55; }
|
||
.note-check {
|
||
background:none; border:none; cursor:pointer; padding:.1rem;
|
||
font-size:1.1rem; flex-shrink:0; margin-top:.1rem;
|
||
}
|
||
.note-circle {
|
||
display:inline-block; width:18px; height:18px; border-radius:50%;
|
||
border:2px solid #8b90a8; vertical-align:middle;
|
||
transition:border-color .15s;
|
||
}
|
||
.note-item:hover .note-circle { border-color:var(--accent); }
|
||
.note-body { flex:1; min-width:0; }
|
||
.note-text {
|
||
font-size:.9rem; color:var(--text); line-height:1.45;
|
||
word-break:break-word;
|
||
}
|
||
.note-done .note-text { text-decoration:line-through; color:#8b90a8; }
|
||
.note-detail {
|
||
font-size:.8rem; color:#8b90a8; margin-top:.3rem; line-height:1.5;
|
||
}
|
||
.note-meta {
|
||
font-size:.72rem; color:#5a5f78; margin-top:.35rem;
|
||
}
|
||
.note-del {
|
||
background:none; border:none; color:#5a5f78; cursor:pointer;
|
||
font-size:.85rem; padding:.1rem .2rem; flex-shrink:0;
|
||
opacity:0; transition:opacity .15s, color .15s;
|
||
}
|
||
.note-item:hover .note-del { opacity:1; }
|
||
.note-del:hover { color:#ef4444; }
|
||
.btn-clear {
|
||
background:none; border:1px solid var(--border); color:#8b90a8;
|
||
border-radius:5px; padding:.3rem .7rem; font-size:.75rem;
|
||
cursor:pointer; transition:all .15s;
|
||
}
|
||
.btn-clear:hover { border-color:#ef4444; color:#ef4444; }
|
||
@media(max-width:600px){
|
||
.notes-wrap { padding:0 1rem 2rem; }
|
||
.notes-add-row { flex-direction:column; align-items:stretch; }
|
||
}
|
||
</style>
|
||
|
||
<div class="notes-wrap">
|
||
<div class="notes-header">
|
||
<div class="notes-title">📝 Notes</div>
|
||
<button class="btn-clear" onclick="clearDone()">Clear completed</button>
|
||
</div>
|
||
|
||
<div class="notes-add">
|
||
<div class="notes-add-row">
|
||
<input id="note-input" type="text" placeholder="Add a note — press Enter to save…">
|
||
<button class="btn-note-add" onclick="addNote()">Add</button>
|
||
</div>
|
||
<button id="note-detail-toggle" onclick="toggleDetail()">+ Add details</button>
|
||
<textarea id="note-detail" placeholder="Additional details…"></textarea>
|
||
</div>
|
||
|
||
<div id="notes-list"></div>
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|