Skip to content

Instantly share code, notes, and snippets.

@RajChowdhury240
Created March 6, 2026 16:51
Show Gist options
  • Select an option

  • Save RajChowdhury240/762023d2c32a85249401c2f849892d7c to your computer and use it in GitHub Desktop.

Select an option

Save RajChowdhury240/762023d2c32a85249401c2f849892d7c to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nexus Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=Manrope:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-root: #06060C;
--bg-surface: #0D0D16;
--bg-card: rgba(14, 14, 24, 0.7);
--bg-card-hover: rgba(20, 20, 34, 0.8);
--border-subtle: rgba(255, 255, 255, 0.04);
--border-glow: rgba(240, 180, 41, 0.15);
--accent-amber: #F0B429;
--accent-amber-dim: rgba(240, 180, 41, 0.12);
--accent-teal: #2DD4BF;
--accent-teal-dim: rgba(45, 212, 191, 0.1);
--accent-coral: #FF6B6B;
--accent-coral-dim: rgba(255, 107, 107, 0.1);
--accent-violet: #A78BFA;
--accent-violet-dim: rgba(167, 139, 250, 0.1);
--text-primary: #E8E6F0;
--text-secondary: rgba(232, 230, 240, 0.55);
--text-tertiary: rgba(232, 230, 240, 0.3);
--font-display: 'Syne', sans-serif;
--font-body: 'Manrope', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--radius-sm: 8px;
--radius-md: 14px;
--radius-lg: 20px;
}
html { font-size: 15px; }
body {
font-family: var(--font-body);
background: var(--bg-root);
color: var(--text-primary);
min-height: 100vh;
overflow-x: hidden;
}
/* Ambient background */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 80% 60% at 20% 10%, rgba(240, 180, 41, 0.03) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 80% 80%, rgba(45, 212, 191, 0.02) 0%, transparent 50%),
radial-gradient(ellipse 40% 40% at 50% 50%, rgba(167, 139, 250, 0.015) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
/* Noise texture overlay */
body::after {
content: '';
position: fixed;
inset: 0;
opacity: 0.35;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
}
#app { position: relative; z-index: 1; }
/* Scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.15); }
/* ============ LAYOUT ============ */
.dashboard { display: flex; min-height: 100vh; }
/* Sidebar */
.sidebar {
width: 260px;
min-height: 100vh;
background: var(--bg-surface);
border-right: 1px solid var(--border-subtle);
padding: 28px 20px;
display: flex;
flex-direction: column;
position: fixed;
left: 0;
top: 0;
bottom: 0;
z-index: 10;
}
.logo {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 40px;
padding: 0 4px;
}
.logo-icon {
width: 36px;
height: 36px;
background: linear-gradient(135deg, var(--accent-amber), #E07C24);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 20px rgba(240, 180, 41, 0.2);
}
.logo-icon svg { width: 20px; height: 20px; }
.logo-text {
font-family: var(--font-display);
font-weight: 700;
font-size: 1.25rem;
letter-spacing: -0.02em;
}
.nav-section {
margin-bottom: 32px;
}
.nav-label {
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--text-tertiary);
padding: 0 12px;
margin-bottom: 10px;
}
.nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
border-radius: var(--radius-sm);
cursor: pointer;
transition: all 0.2s ease;
color: var(--text-secondary);
font-size: 0.88rem;
font-weight: 500;
position: relative;
}
.nav-item:hover {
background: rgba(255, 255, 255, 0.03);
color: var(--text-primary);
}
.nav-item.active {
background: var(--accent-amber-dim);
color: var(--accent-amber);
}
.nav-item.active::before {
content: '';
position: absolute;
left: -20px;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 20px;
background: var(--accent-amber);
border-radius: 0 3px 3px 0;
}
.nav-item svg { width: 18px; height: 18px; opacity: 0.7; flex-shrink: 0; }
.nav-item.active svg { opacity: 1; }
.nav-badge {
margin-left: auto;
background: var(--accent-coral);
color: #fff;
font-size: 0.65rem;
font-weight: 700;
padding: 2px 7px;
border-radius: 10px;
font-family: var(--font-mono);
}
.sidebar-footer {
margin-top: auto;
padding: 16px 12px;
border-top: 1px solid var(--border-subtle);
display: flex;
align-items: center;
gap: 12px;
}
.avatar {
width: 34px;
height: 34px;
border-radius: 10px;
background: linear-gradient(135deg, var(--accent-teal), var(--accent-violet));
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-display);
font-weight: 700;
font-size: 0.8rem;
color: #fff;
}
.user-info { flex: 1; min-width: 0; }
.user-name { font-size: 0.85rem; font-weight: 600; }
.user-role { font-size: 0.7rem; color: var(--text-tertiary); }
/* Main content */
.main {
flex: 1;
margin-left: 260px;
padding: 28px 32px 40px;
}
/* Top bar */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 32px;
}
.topbar-left h1 {
font-family: var(--font-display);
font-size: 1.8rem;
font-weight: 800;
letter-spacing: -0.03em;
line-height: 1.1;
}
.topbar-left p {
color: var(--text-secondary);
font-size: 0.85rem;
margin-top: 4px;
}
.topbar-right {
display: flex;
align-items: center;
gap: 12px;
}
.search-box {
display: flex;
align-items: center;
gap: 8px;
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
padding: 8px 14px;
width: 240px;
transition: border-color 0.2s;
}
.search-box:focus-within { border-color: var(--border-glow); }
.search-box svg { width: 16px; height: 16px; color: var(--text-tertiary); flex-shrink: 0; }
.search-box input {
background: none;
border: none;
outline: none;
color: var(--text-primary);
font-family: var(--font-body);
font-size: 0.82rem;
width: 100%;
}
.search-box input::placeholder { color: var(--text-tertiary); }
.icon-btn {
width: 38px;
height: 38px;
border-radius: var(--radius-sm);
background: var(--bg-card);
border: 1px solid var(--border-subtle);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
position: relative;
}
.icon-btn:hover { background: var(--bg-card-hover); border-color: var(--border-glow); }
.icon-btn svg { width: 17px; height: 17px; color: var(--text-secondary); }
.icon-btn .notif-dot {
position: absolute;
top: 7px;
right: 8px;
width: 7px;
height: 7px;
background: var(--accent-coral);
border-radius: 50%;
border: 2px solid var(--bg-surface);
}
/* Widget Grid */
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 18px;
}
.widget {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
padding: 22px;
backdrop-filter: blur(20px);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.widget::before {
content: '';
position: absolute;
inset: 0;
border-radius: var(--radius-lg);
padding: 1px;
background: linear-gradient(135deg, rgba(255,255,255,0.05), transparent 50%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
.widget:hover { border-color: rgba(255, 255, 255, 0.06); transform: translateY(-1px); }
.col-3 { grid-column: span 3; }
.col-4 { grid-column: span 4; }
.col-5 { grid-column: span 5; }
.col-6 { grid-column: span 6; }
.col-7 { grid-column: span 7; }
.col-8 { grid-column: span 8; }
.col-12 { grid-column: span 12; }
.widget-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
}
.widget-title {
font-family: var(--font-display);
font-size: 0.95rem;
font-weight: 700;
letter-spacing: -0.01em;
}
.widget-subtitle {
font-size: 0.72rem;
color: var(--text-tertiary);
margin-top: 2px;
}
.widget-action {
font-size: 0.72rem;
color: var(--accent-amber);
cursor: pointer;
font-weight: 600;
transition: opacity 0.2s;
}
.widget-action:hover { opacity: 0.8; }
/* ============ STAT CARDS ============ */
.stat-cards { display: flex; gap: 18px; grid-column: span 12; }
.stat-card {
flex: 1;
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 20px;
position: relative;
overflow: hidden;
transition: all 0.3s;
}
.stat-card:hover { border-color: rgba(255,255,255,0.06); }
.stat-card-icon {
width: 40px;
height: 40px;
border-radius: 11px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 14px;
}
.stat-card-icon svg { width: 20px; height: 20px; }
.stat-card-icon.amber { background: var(--accent-amber-dim); color: var(--accent-amber); }
.stat-card-icon.teal { background: var(--accent-teal-dim); color: var(--accent-teal); }
.stat-card-icon.coral { background: var(--accent-coral-dim); color: var(--accent-coral); }
.stat-card-icon.violet { background: var(--accent-violet-dim); color: var(--accent-violet); }
.stat-card-value {
font-family: var(--font-mono);
font-size: 1.65rem;
font-weight: 600;
letter-spacing: -0.03em;
margin-bottom: 4px;
}
.stat-card-label {
font-size: 0.75rem;
color: var(--text-secondary);
}
.stat-card-change {
position: absolute;
top: 20px;
right: 20px;
font-family: var(--font-mono);
font-size: 0.72rem;
font-weight: 600;
padding: 3px 8px;
border-radius: 6px;
}
.stat-card-change.up { background: rgba(45, 212, 191, 0.1); color: var(--accent-teal); }
.stat-card-change.down { background: rgba(255, 107, 107, 0.1); color: var(--accent-coral); }
/* Sparkline in stat card */
.stat-sparkline {
margin-top: 12px;
height: 32px;
}
.stat-sparkline svg { width: 100%; height: 100%; }
/* ============ ANALYTICS CHART ============ */
.chart-container { height: 220px; position: relative; }
.chart-tabs {
display: flex;
gap: 4px;
background: rgba(255,255,255,0.03);
border-radius: 8px;
padding: 3px;
}
.chart-tab {
font-size: 0.72rem;
font-weight: 600;
padding: 5px 14px;
border-radius: 6px;
cursor: pointer;
color: var(--text-tertiary);
transition: all 0.2s;
}
.chart-tab.active { background: var(--accent-amber-dim); color: var(--accent-amber); }
.chart-tab:hover:not(.active) { color: var(--text-secondary); }
.chart-svg { width: 100%; height: 100%; }
.chart-grid-line {
stroke: rgba(255,255,255,0.03);
stroke-width: 1;
}
.chart-label {
font-family: var(--font-mono);
font-size: 10px;
fill: var(--text-tertiary);
}
.chart-line {
fill: none;
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.chart-area { opacity: 0.15; }
.chart-dot {
stroke-width: 2;
transition: r 0.2s;
cursor: pointer;
}
.chart-dot:hover { r: 6; }
/* ============ SYSTEM STATS ============ */
.system-stats { display: flex; flex-direction: column; gap: 20px; }
.gauge-row {
display: flex;
align-items: center;
gap: 16px;
}
.gauge-ring {
width: 52px;
height: 52px;
flex-shrink: 0;
position: relative;
}
.gauge-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.gauge-bg {
fill: none;
stroke: rgba(255,255,255,0.04);
stroke-width: 4;
}
.gauge-fill {
fill: none;
stroke-width: 4;
stroke-linecap: round;
transition: stroke-dashoffset 1s ease;
}
.gauge-value {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 0.68rem;
font-weight: 600;
}
.gauge-info { flex: 1; min-width: 0; }
.gauge-label {
font-size: 0.82rem;
font-weight: 600;
margin-bottom: 3px;
}
.gauge-detail {
font-size: 0.7rem;
color: var(--text-tertiary);
font-family: var(--font-mono);
}
/* ============ ACTIVITY FEED ============ */
.activity-list { display: flex; flex-direction: column; gap: 2px; }
.activity-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--border-subtle);
position: relative;
}
.activity-item:last-child { border-bottom: none; }
.activity-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 5px;
flex-shrink: 0;
}
.activity-dot.amber { background: var(--accent-amber); box-shadow: 0 0 8px rgba(240, 180, 41, 0.3); }
.activity-dot.teal { background: var(--accent-teal); box-shadow: 0 0 8px rgba(45, 212, 191, 0.3); }
.activity-dot.coral { background: var(--accent-coral); box-shadow: 0 0 8px rgba(255, 107, 107, 0.3); }
.activity-dot.violet { background: var(--accent-violet); box-shadow: 0 0 8px rgba(167, 139, 250, 0.3); }
.activity-content { flex: 1; min-width: 0; }
.activity-text {
font-size: 0.82rem;
line-height: 1.5;
color: var(--text-secondary);
}
.activity-text strong { color: var(--text-primary); font-weight: 600; }
.activity-time {
font-size: 0.68rem;
color: var(--text-tertiary);
font-family: var(--font-mono);
margin-top: 3px;
}
/* ============ TASKS ============ */
.task-list { display: flex; flex-direction: column; gap: 6px; }
.task-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
border-radius: var(--radius-sm);
transition: background 0.2s;
cursor: pointer;
}
.task-item:hover { background: rgba(255,255,255,0.02); }
.task-check {
width: 20px;
height: 20px;
border-radius: 6px;
border: 2px solid rgba(255,255,255,0.12);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s;
cursor: pointer;
}
.task-check.done {
background: var(--accent-teal);
border-color: var(--accent-teal);
}
.task-check.done svg { opacity: 1; }
.task-check svg { width: 12px; height: 12px; opacity: 0; color: #fff; }
.task-text {
flex: 1;
font-size: 0.82rem;
font-weight: 500;
}
.task-text.done {
text-decoration: line-through;
color: var(--text-tertiary);
}
.task-priority {
font-size: 0.62rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 3px 8px;
border-radius: 5px;
}
.task-priority.high { background: var(--accent-coral-dim); color: var(--accent-coral); }
.task-priority.medium { background: var(--accent-amber-dim); color: var(--accent-amber); }
.task-priority.low { background: var(--accent-teal-dim); color: var(--accent-teal); }
/* ============ WEATHER ============ */
.weather-main {
text-align: center;
padding: 10px 0 16px;
}
.weather-icon {
font-size: 3.2rem;
margin-bottom: 8px;
line-height: 1;
}
.weather-temp {
font-family: var(--font-display);
font-size: 2.8rem;
font-weight: 800;
letter-spacing: -0.04em;
background: linear-gradient(135deg, var(--accent-amber), #E07C24);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.weather-desc {
font-size: 0.85rem;
color: var(--text-secondary);
margin-top: 2px;
}
.weather-loc {
font-size: 0.72rem;
color: var(--text-tertiary);
font-family: var(--font-mono);
}
.weather-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--border-subtle);
}
.weather-detail {
text-align: center;
}
.weather-detail-val {
font-family: var(--font-mono);
font-size: 0.9rem;
font-weight: 600;
}
.weather-detail-label {
font-size: 0.65rem;
color: var(--text-tertiary);
margin-top: 2px;
}
/* ============ CALENDAR ============ */
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
text-align: center;
}
.calendar-dow {
font-size: 0.62rem;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 4px 0 8px;
}
.calendar-day {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
font-family: var(--font-mono);
border-radius: 8px;
cursor: pointer;
transition: all 0.15s;
color: var(--text-secondary);
position: relative;
}
.calendar-day:hover { background: rgba(255,255,255,0.04); }
.calendar-day.other { color: var(--text-tertiary); opacity: 0.4; }
.calendar-day.today {
background: var(--accent-amber);
color: #0A0A0F;
font-weight: 700;
box-shadow: 0 0 16px rgba(240, 180, 41, 0.3);
}
.calendar-day.has-event::after {
content: '';
position: absolute;
bottom: 3px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--accent-teal);
}
.calendar-month {
font-family: var(--font-display);
font-size: 1rem;
font-weight: 700;
}
.calendar-nav {
display: flex;
align-items: center;
gap: 8px;
}
.calendar-nav button {
width: 28px;
height: 28px;
border-radius: 7px;
border: 1px solid var(--border-subtle);
background: transparent;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
}
.calendar-nav button:hover { background: rgba(255,255,255,0.04); }
.calendar-nav button svg { width: 14px; height: 14px; }
.upcoming-events { margin-top: 16px; display: flex; flex-direction: column; gap: 8px; }
.event-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border-radius: var(--radius-sm);
background: rgba(255,255,255,0.02);
}
.event-bar {
width: 3px;
height: 32px;
border-radius: 2px;
flex-shrink: 0;
}
.event-bar.amber { background: var(--accent-amber); }
.event-bar.teal { background: var(--accent-teal); }
.event-bar.violet { background: var(--accent-violet); }
.event-info { flex: 1; }
.event-name { font-size: 0.78rem; font-weight: 600; }
.event-time {
font-size: 0.65rem;
color: var(--text-tertiary);
font-family: var(--font-mono);
}
/* ============ ANIMATIONS ============ */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes pulseGlow {
0%, 100% { box-shadow: 0 0 8px rgba(240, 180, 41, 0.15); }
50% { box-shadow: 0 0 16px rgba(240, 180, 41, 0.3); }
}
@keyframes drawLine {
from { stroke-dashoffset: 1000; }
to { stroke-dashoffset: 0; }
}
.sidebar { animation: slideInLeft 0.5s ease both; }
.stat-card {
animation: fadeInUp 0.5s ease both;
}
.stat-card:nth-child(1) { animation-delay: 0.1s; }
.stat-card:nth-child(2) { animation-delay: 0.17s; }
.stat-card:nth-child(3) { animation-delay: 0.24s; }
.stat-card:nth-child(4) { animation-delay: 0.31s; }
.widget {
animation: fadeInUp 0.5s ease both;
}
.grid > :nth-child(2) { animation-delay: 0.15s; }
.grid > :nth-child(3) { animation-delay: 0.22s; }
.grid > :nth-child(4) { animation-delay: 0.29s; }
.grid > :nth-child(5) { animation-delay: 0.36s; }
.grid > :nth-child(6) { animation-delay: 0.43s; }
.chart-line {
stroke-dasharray: 1000;
animation: drawLine 1.5s ease forwards;
animation-delay: 0.5s;
stroke-dashoffset: 1000;
}
/* ============ RESPONSIVE ============ */
@media (max-width: 1200px) {
.col-3 { grid-column: span 6; }
.col-4 { grid-column: span 6; }
.col-5 { grid-column: span 12; }
.col-7 { grid-column: span 12; }
.col-8 { grid-column: span 12; }
}
@media (max-width: 768px) {
.sidebar { display: none; }
.main { margin-left: 0; padding: 20px 16px; }
.stat-cards { flex-direction: column; }
.col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-12 { grid-column: span 12; }
.topbar { flex-direction: column; align-items: flex-start; gap: 16px; }
.search-box { width: 100%; }
}
</style>
</head>
<body>
<div id="app">
<div class="dashboard">
<!-- Sidebar -->
<aside class="sidebar">
<div class="logo">
<div class="logo-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2"/>
<line x1="12" y1="22" x2="12" y2="15.5"/>
<polyline points="22 8.5 12 15.5 2 8.5"/>
</svg>
</div>
<span class="logo-text">Nexus</span>
</div>
<nav>
<div class="nav-section">
<div class="nav-label">Overview</div>
<div class="nav-item active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
Dashboard
</div>
<div class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.21 15.89A10 10 0 1 1 8 2.83"/><path d="M22 12A10 10 0 0 0 12 2v10z"/></svg>
Analytics
</div>
<div class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
Users
<span class="nav-badge">3</span>
</div>
</div>
<div class="nav-section">
<div class="nav-label">Workspace</div>
<div class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
Documents
</div>
<div class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg>
Inbox
<span class="nav-badge">12</span>
</div>
<div class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
Settings
</div>
</div>
</nav>
<div class="sidebar-footer">
<div class="avatar">RK</div>
<div class="user-info">
<div class="user-name">Raj Kumar</div>
<div class="user-role">Administrator</div>
</div>
</div>
</aside>
<!-- Main -->
<main class="main">
<div class="topbar">
<div class="topbar-left">
<h1>Dashboard</h1>
<p>Welcome back. Here's what's happening today.</p>
</div>
<div class="topbar-right">
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Search anything..." />
</div>
<div class="icon-btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
<span class="notif-dot"></span>
</div>
</div>
</div>
<!-- Stat cards -->
<div class="stat-cards">
<div class="stat-card" v-for="(stat, i) in stats" :key="i">
<div :class="['stat-card-icon', stat.color]">
<svg v-if="stat.icon==='revenue'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
<svg v-if="stat.icon==='users'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
<svg v-if="stat.icon==='orders'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg>
<svg v-if="stat.icon==='uptime'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
</div>
<div class="stat-card-value">{{ stat.value }}</div>
<div class="stat-card-label">{{ stat.label }}</div>
<div :class="['stat-card-change', stat.changeDir]">{{ stat.change }}</div>
<div class="stat-sparkline">
<svg viewBox="0 0 120 32" preserveAspectRatio="none">
<polyline :points="stat.sparkline" fill="none" :stroke="stat.strokeColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
</div>
<div style="height: 18px"></div>
<!-- Widget Grid -->
<div class="grid">
<!-- Analytics Chart -->
<div class="widget col-8">
<div class="widget-header">
<div>
<div class="widget-title">Revenue Analytics</div>
<div class="widget-subtitle">Monthly performance overview</div>
</div>
<div class="chart-tabs">
<div v-for="tab in ['7D','1M','3M','1Y']" :key="tab"
:class="['chart-tab', { active: chartTab === tab }]"
@click="chartTab = tab">{{ tab }}</div>
</div>
</div>
<div class="chart-container">
<svg class="chart-svg" :viewBox="'0 0 ' + chartW + ' ' + chartH">
<!-- Grid lines -->
<line v-for="i in 5" :key="'g'+i" class="chart-grid-line"
:x1="chartPad" :x2="chartW - 10"
:y1="chartPad + (i-1) * ((chartH - chartPad*2) / 4)"
:y2="chartPad + (i-1) * ((chartH - chartPad*2) / 4)"/>
<!-- Y labels -->
<text v-for="(label, i) in yLabels" :key="'yl'+i" class="chart-label" text-anchor="end"
:x="chartPad - 8"
:y="chartPad + i * ((chartH - chartPad*2) / 4) + 4">{{ label }}</text>
<!-- X labels -->
<text v-for="(label, i) in xLabels" :key="'xl'+i" class="chart-label" text-anchor="middle"
:x="chartPad + i * ((chartW - chartPad - 10) / (xLabels.length - 1))"
:y="chartH - 4">{{ label }}</text>
<!-- Area fill -->
<path :d="areaPath" class="chart-area" :fill="'url(#areaGrad)'"/>
<defs>
<linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#F0B429" stop-opacity="0.4"/>
<stop offset="100%" stop-color="#F0B429" stop-opacity="0"/>
</linearGradient>
<linearGradient id="lineGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#F0B429"/>
<stop offset="100%" stop-color="#E07C24"/>
</linearGradient>
<linearGradient id="areaGrad2" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#2DD4BF" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#2DD4BF" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- Line 2 (secondary) -->
<path :d="linePath2" class="chart-line" stroke="#2DD4BF" stroke-opacity="0.5"/>
<path :d="areaPath2" class="chart-area" fill="url(#areaGrad2)"/>
<!-- Line 1 (primary) -->
<path :d="linePath" class="chart-line" stroke="url(#lineGrad)"/>
<!-- Dots -->
<circle v-for="(pt, i) in chartPoints" :key="'d'+i"
class="chart-dot" :cx="pt.x" :cy="pt.y" r="4"
fill="#0A0A0F" stroke="#F0B429" />
</svg>
</div>
</div>
<!-- System Stats -->
<div class="widget col-4">
<div class="widget-header">
<div>
<div class="widget-title">System Health</div>
<div class="widget-subtitle">Server performance metrics</div>
</div>
<div class="widget-action">Details</div>
</div>
<div class="system-stats">
<div class="gauge-row" v-for="(g, i) in gauges" :key="i">
<div class="gauge-ring">
<svg viewBox="0 0 48 48">
<circle class="gauge-bg" cx="24" cy="24" r="20"/>
<circle class="gauge-fill" cx="24" cy="24" r="20"
:stroke="g.color"
:stroke-dasharray="125.6"
:stroke-dashoffset="125.6 - (125.6 * g.value / 100)"/>
</svg>
<div class="gauge-value" :style="{ color: g.color }">{{ g.value }}%</div>
</div>
<div class="gauge-info">
<div class="gauge-label">{{ g.label }}</div>
<div class="gauge-detail">{{ g.detail }}</div>
</div>
</div>
</div>
</div>
<!-- Activity Feed -->
<div class="widget col-5">
<div class="widget-header">
<div>
<div class="widget-title">Activity Feed</div>
<div class="widget-subtitle">Recent events and updates</div>
</div>
<div class="widget-action">View all</div>
</div>
<div class="activity-list">
<div class="activity-item" v-for="(a, i) in activities" :key="i">
<div :class="['activity-dot', a.color]"></div>
<div class="activity-content">
<div class="activity-text" v-html="a.text"></div>
<div class="activity-time">{{ a.time }}</div>
</div>
</div>
</div>
</div>
<!-- Tasks -->
<div class="widget col-4">
<div class="widget-header">
<div>
<div class="widget-title">Tasks</div>
<div class="widget-subtitle">{{ completedTasks }}/{{ tasks.length }} completed</div>
</div>
<div class="widget-action">+ Add</div>
</div>
<div class="task-list">
<div class="task-item" v-for="(t, i) in tasks" :key="i" @click="t.done = !t.done">
<div :class="['task-check', { done: t.done }]">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div :class="['task-text', { done: t.done }]">{{ t.text }}</div>
<div :class="['task-priority', t.priority]">{{ t.priority }}</div>
</div>
</div>
</div>
<!-- Weather -->
<div class="widget col-3">
<div class="widget-header">
<div class="widget-title">Weather</div>
</div>
<div class="weather-main">
<div class="weather-icon">{{ weather.icon }}</div>
<div class="weather-temp">{{ weather.temp }}</div>
<div class="weather-desc">{{ weather.desc }}</div>
<div class="weather-loc">{{ weather.location }}</div>
</div>
<div class="weather-details">
<div class="weather-detail" v-for="(d, i) in weather.details" :key="i">
<div class="weather-detail-val">{{ d.value }}</div>
<div class="weather-detail-label">{{ d.label }}</div>
</div>
</div>
</div>
<!-- Calendar -->
<div class="widget col-5">
<div class="widget-header">
<div class="calendar-month">{{ monthName }} {{ year }}</div>
<div class="calendar-nav">
<button @click="prevMonth">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
</button>
<button @click="nextMonth">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
</button>
</div>
</div>
<div class="calendar-grid">
<div class="calendar-dow" v-for="d in dows" :key="d">{{ d }}</div>
<div v-for="(day, i) in calendarDays" :key="i"
:class="['calendar-day', {
other: day.other,
today: day.isToday,
'has-event': day.hasEvent
}]">{{ day.num }}</div>
</div>
<div class="upcoming-events">
<div class="event-item" v-for="(e, i) in events" :key="i">
<div :class="['event-bar', e.color]"></div>
<div class="event-info">
<div class="event-name">{{ e.name }}</div>
<div class="event-time">{{ e.time }}</div>
</div>
</div>
</div>
</div>
<!-- Quick Chart - Bar chart -->
<div class="widget col-7">
<div class="widget-header">
<div>
<div class="widget-title">Traffic Sources</div>
<div class="widget-subtitle">Visitors by channel this week</div>
</div>
<div class="widget-action">Export</div>
</div>
<div style="display: flex; gap: 30px; align-items: flex-end; height: 160px; padding-top: 10px;">
<div v-for="(bar, i) in trafficBars" :key="i"
style="flex: 1; display: flex; flex-direction: column; align-items: center; gap: 8px;">
<div style="font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-secondary);">
{{ bar.value }}
</div>
<div :style="{
width: '100%',
maxWidth: '48px',
height: bar.height + '%',
background: 'linear-gradient(to top, ' + bar.colorFrom + ', ' + bar.colorTo + ')',
borderRadius: '6px 6px 4px 4px',
transition: 'height 0.8s ease',
minHeight: '4px'
}"></div>
<div style="font-size: 0.68rem; color: var(--text-tertiary); white-space: nowrap;">
{{ bar.label }}
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script>
const { createApp, ref, computed, onMounted } = Vue
createApp({
setup() {
// Stat cards
const stats = ref([
{
icon: 'revenue', label: 'Total Revenue', value: '$48,295',
change: '+12.5%', changeDir: 'up', color: 'amber',
strokeColor: '#F0B429',
sparkline: '0,28 15,24 30,26 45,18 60,20 75,12 90,14 105,8 120,4'
},
{
icon: 'users', label: 'Active Users', value: '2,847',
change: '+8.2%', changeDir: 'up', color: 'teal',
strokeColor: '#2DD4BF',
sparkline: '0,24 15,22 30,18 45,20 60,14 75,16 90,10 105,12 120,6'
},
{
icon: 'orders', label: 'New Orders', value: '1,024',
change: '-2.4%', changeDir: 'down', color: 'coral',
strokeColor: '#FF6B6B',
sparkline: '0,8 15,12 30,10 45,16 60,14 75,20 90,18 105,24 120,22'
},
{
icon: 'uptime', label: 'Uptime', value: '99.97%',
change: '+0.02%', changeDir: 'up', color: 'violet',
strokeColor: '#A78BFA',
sparkline: '0,6 15,4 30,6 45,4 60,5 75,4 90,3 105,4 120,2'
}
])
// Chart
const chartTab = ref('1M')
const chartW = 620
const chartH = 220
const chartPad = 45
const chartData = ref([32, 45, 38, 62, 55, 72, 68, 85, 78, 92, 88, 95])
const chartData2 = ref([20, 28, 25, 40, 35, 48, 42, 55, 50, 60, 56, 62])
const yLabels = ['$100K', '$75K', '$50K', '$25K', '$0']
const xLabels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
const getPoint = (data, index, total) => {
const x = chartPad + index * ((chartW - chartPad - 10) / (total - 1))
const y = chartPad + (1 - data / 100) * (chartH - chartPad * 2)
return { x, y }
}
const chartPoints = computed(() =>
chartData.value.map((d, i) => getPoint(d, i, chartData.value.length))
)
const linePath = computed(() =>
chartPoints.value.map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join(' ')
)
const chartPoints2 = computed(() =>
chartData2.value.map((d, i) => getPoint(d, i, chartData2.value.length))
)
const linePath2 = computed(() =>
chartPoints2.value.map((p, i) => (i === 0 ? 'M' : 'L') + p.x + ',' + p.y).join(' ')
)
const areaPath = computed(() => {
const pts = chartPoints.value
if (!pts.length) return ''
const bottom = chartH - chartPad
return `M${pts[0].x},${bottom} ` +
pts.map(p => `L${p.x},${p.y}`).join(' ') +
` L${pts[pts.length-1].x},${bottom} Z`
})
const areaPath2 = computed(() => {
const pts = chartPoints2.value
if (!pts.length) return ''
const bottom = chartH - chartPad
return `M${pts[0].x},${bottom} ` +
pts.map(p => `L${p.x},${p.y}`).join(' ') +
` L${pts[pts.length-1].x},${bottom} Z`
})
// System Stats
const gauges = ref([
{ label: 'CPU Usage', value: 67, detail: '4.2 GHz / 8 cores', color: '#F0B429' },
{ label: 'Memory', value: 82, detail: '13.1 GB / 16 GB', color: '#FF6B6B' },
{ label: 'Storage', value: 45, detail: '461 GB / 1 TB', color: '#2DD4BF' },
{ label: 'Network', value: 34, detail: '340 Mbps / 1 Gbps', color: '#A78BFA' },
{ label: 'GPU', value: 58, detail: '12.4 GB / 24 GB VRAM', color: '#F0B429' }
])
// Activity Feed
const activities = ref([
{ text: '<strong>Sarah Chen</strong> deployed <strong>v2.4.1</strong> to production', time: '2 min ago', color: 'amber' },
{ text: '<strong>API Gateway</strong> latency spike detected (>200ms)', time: '15 min ago', color: 'coral' },
{ text: '<strong>Marcus Wei</strong> merged PR #847 into main', time: '32 min ago', color: 'teal' },
{ text: '<strong>Auto-scaler</strong> added 2 instances to us-east cluster', time: '1 hour ago', color: 'violet' },
{ text: '<strong>CI Pipeline</strong> build #1204 passed all 342 tests', time: '1.5 hours ago', color: 'teal' },
{ text: '<strong>Aisha Patel</strong> updated DNS records for staging', time: '3 hours ago', color: 'amber' }
])
// Tasks
const tasks = ref([
{ text: 'Review authentication flow PR', done: false, priority: 'high' },
{ text: 'Update API rate limiting config', done: false, priority: 'high' },
{ text: 'Migrate database to v3 schema', done: true, priority: 'medium' },
{ text: 'Write integration test suite', done: false, priority: 'medium' },
{ text: 'Optimize image CDN caching', done: true, priority: 'low' },
{ text: 'Document webhook endpoints', done: false, priority: 'low' }
])
const completedTasks = computed(() => tasks.value.filter(t => t.done).length)
// Weather
const weather = ref({
icon: '\u2600\uFE0F',
temp: '24\u00B0C',
desc: 'Clear Sky',
location: '40.7128\u00B0N, 74.0060\u00B0W',
details: [
{ value: '62%', label: 'Humidity' },
{ value: '12 km/h', label: 'Wind' },
{ value: '1013 hPa', label: 'Pressure' },
{ value: '10 km', label: 'Visibility' }
]
})
// Calendar
const now = new Date()
const calMonth = ref(now.getMonth())
const calYear = ref(now.getFullYear())
const monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December']
const dows = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
const monthName = computed(() => monthNames[calMonth.value])
const year = computed(() => calYear.value)
const eventDays = [6, 12, 15, 22, 28]
const calendarDays = computed(() => {
const days = []
const firstDay = new Date(calYear.value, calMonth.value, 1)
let startDow = firstDay.getDay() - 1
if (startDow < 0) startDow = 6
const prevMonthDays = new Date(calYear.value, calMonth.value, 0).getDate()
for (let i = startDow - 1; i >= 0; i--) {
days.push({ num: prevMonthDays - i, other: true, isToday: false, hasEvent: false })
}
const daysInMonth = new Date(calYear.value, calMonth.value + 1, 0).getDate()
for (let d = 1; d <= daysInMonth; d++) {
const isToday = d === now.getDate() && calMonth.value === now.getMonth() && calYear.value === now.getFullYear()
days.push({ num: d, other: false, isToday, hasEvent: eventDays.includes(d) })
}
const remaining = 42 - days.length
for (let d = 1; d <= remaining; d++) {
days.push({ num: d, other: true, isToday: false, hasEvent: false })
}
return days
})
const prevMonth = () => {
if (calMonth.value === 0) { calMonth.value = 11; calYear.value-- }
else calMonth.value--
}
const nextMonth = () => {
if (calMonth.value === 11) { calMonth.value = 0; calYear.value++ }
else calMonth.value++
}
const events = ref([
{ name: 'Sprint Planning', time: 'Today, 10:00 AM', color: 'amber' },
{ name: 'Design Review', time: 'Today, 2:30 PM', color: 'teal' },
{ name: 'Team Standup', time: 'Tomorrow, 9:00 AM', color: 'violet' }
])
// Traffic Bars
const trafficBars = ref([
{ label: 'Direct', value: '4.2K', height: 72, colorFrom: 'rgba(240,180,41,0.6)', colorTo: '#F0B429' },
{ label: 'Organic', value: '3.8K', height: 65, colorFrom: 'rgba(45,212,191,0.6)', colorTo: '#2DD4BF' },
{ label: 'Referral', value: '2.1K', height: 38, colorFrom: 'rgba(167,139,250,0.6)', colorTo: '#A78BFA' },
{ label: 'Social', value: '1.9K', height: 34, colorFrom: 'rgba(255,107,107,0.6)', colorTo: '#FF6B6B' },
{ label: 'Email', value: '1.4K', height: 26, colorFrom: 'rgba(240,180,41,0.6)', colorTo: '#F0B429' },
{ label: 'Paid', value: '3.1K', height: 54, colorFrom: 'rgba(45,212,191,0.6)', colorTo: '#2DD4BF' },
{ label: 'Display', value: '0.8K', height: 16, colorFrom: 'rgba(167,139,250,0.6)', colorTo: '#A78BFA' }
])
return {
stats, chartTab, chartW, chartH, chartPad,
yLabels, xLabels, chartPoints, linePath, linePath2,
areaPath, areaPath2,
gauges, activities, tasks, completedTasks,
weather, calMonth, calYear, monthName, year, dows,
calendarDays, prevMonth, nextMonth, events,
trafficBars
}
}
}).mount('#app')
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment