- Login page with animated floating stat cards - Executive dashboard with Chart.js KPIs and activity feed - Full asset registry (list, search, filter, bulk actions, QR) - Asset detail page with 6 tabs (financial, maintenance, history…) - 3-step asset creation wizard with category-specific fields - Inventory: stock overview, GRN, location tree, physical audit - Procurement: PR → approval → PO → GRN → asset lifecycle - Maintenance: Kanban board, PM schedule, AMC contracts - Reports: depreciation schedule, utilization, compliance gauge - User management: roles, permission matrix, session control - Settings: 8 config sections (org, depreciation, security, integrations) - Premium dark UI with CSS variables, Chart.js 4, toast system
324 lines
15 KiB
HTML
324 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>Dashboard | AMS</title>
|
||
<meta name="description" content="Asset Management System – Executive Dashboard with KPIs and analytics">
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="css/styles.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||
</head>
|
||
<body>
|
||
<div class="app-layout">
|
||
|
||
<!-- ════ SIDEBAR ════ -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-logo">
|
||
<div class="logo-icon">📦</div>
|
||
<div><div class="logo-title">AMS</div><div class="logo-sub">Asset Management</div></div>
|
||
</div>
|
||
<nav class="sidebar-nav">
|
||
<div class="nav-section-label">Overview</div>
|
||
<a href="dashboard.html" class="nav-item active"><span class="nav-icon">📊</span> Dashboard</a>
|
||
<div class="nav-section-label">Assets</div>
|
||
<a href="assets.html" class="nav-item"><span class="nav-icon">📦</span> All Assets</a>
|
||
<a href="asset-create.html" class="nav-item"><span class="nav-icon">➕</span> Add Asset</a>
|
||
<div class="nav-section-label">Supply Chain</div>
|
||
<a href="inventory.html" class="nav-item"><span class="nav-icon">🏪</span> Inventory <span class="nav-badge" style="background:var(--warning)">5</span></a>
|
||
<a href="procurement.html" class="nav-item"><span class="nav-icon">🛒</span> Procurement <span class="nav-badge">8</span></a>
|
||
<div class="nav-section-label">Operations</div>
|
||
<a href="maintenance.html" class="nav-item"><span class="nav-icon">🔧</span> Maintenance <span class="nav-badge">12</span></a>
|
||
<a href="reports.html" class="nav-item"><span class="nav-icon">📈</span> Reports & Audit</a>
|
||
<div class="nav-section-label">Administration</div>
|
||
<a href="users.html" class="nav-item"><span class="nav-icon">👥</span> Users & Roles</a>
|
||
<a href="settings.html" class="nav-item"><span class="nav-icon">⚙️</span> Settings</a>
|
||
</nav>
|
||
<div class="sidebar-footer">
|
||
<div class="user-card">
|
||
<div class="user-av">AS</div>
|
||
<div style="flex:1;min-width:0">
|
||
<div class="user-name">Arjun Sharma</div>
|
||
<div class="user-role">Asset Manager</div>
|
||
</div>
|
||
<span style="color:var(--text-muted)">⋮</span>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ════ MAIN ════ -->
|
||
<div class="main-wrapper">
|
||
|
||
<!-- Topbar -->
|
||
<header class="topbar">
|
||
<div class="topbar-left">
|
||
<div class="topbar-title">Dashboard <span class="topbar-sub">Welcome back, Arjun 👋</span></div>
|
||
</div>
|
||
<div class="topbar-search">
|
||
<span style="color:var(--text-muted)">🔍</span>
|
||
<input type="text" placeholder="Search assets, tickets…" id="globalSearch">
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<div style="position:relative">
|
||
<button class="icon-btn" id="notifBell">🔔<span class="badge-dot"></span></button>
|
||
<div class="notif-panel" id="notifPanel">
|
||
<div class="notif-header"><span class="notif-title">Notifications</span><button class="btn btn-ghost btn-sm" onclick="markAllRead()" style="font-size:11px">Mark all read</button></div>
|
||
<div id="notifList"></div>
|
||
<div style="padding:10px 16px;text-align:center;border-top:1px solid var(--border)"><a href="#" style="font-size:12px;color:var(--primary-light)">View all</a></div>
|
||
</div>
|
||
</div>
|
||
<a href="index.html" class="icon-btn" title="Logout">🚪</a>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="content">
|
||
<!-- ── Alerts ──────────────────────── -->
|
||
<div id="alertsArea" class="mb-4"></div>
|
||
|
||
<!-- ── KPI Cards ──────────────────── -->
|
||
<div class="grid-4 mb-4" id="kpiCards"></div>
|
||
|
||
<!-- ── Charts Row ─────────────────── -->
|
||
<div class="grid-charts mb-4">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">📊 Asset Status Distribution</span>
|
||
<button class="btn btn-ghost btn-sm" onclick="window.location.href='reports.html'">View Report →</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<canvas id="statusChart" height="260"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">🍕 Assets by Category</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<canvas id="categoryChart" height="260"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Second Charts Row ──────────── -->
|
||
<div class="grid-2 mb-4">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">💰 Depreciation Overview</span>
|
||
<button class="btn btn-ghost btn-sm" onclick="window.location.href='reports.html'">Details →</button>
|
||
</div>
|
||
<div class="card-body">
|
||
<canvas id="depreciationChart" height="210"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">⚠️ Action Items</span>
|
||
<span class="badge badge-danger" style="font-size:11px">Needs Attention</span>
|
||
</div>
|
||
<div class="card-body" id="actionItems"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Bottom Row ─────────────────── -->
|
||
<div class="grid-2">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">🕐 Recent Activity</span>
|
||
<span style="font-size:12px;color:var(--text-muted)">Last 7 days</span>
|
||
</div>
|
||
<div class="card-body" id="activityFeed" style="padding-top:8px"></div>
|
||
</div>
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<span class="card-title">📦 Assets by Department</span>
|
||
</div>
|
||
<div class="card-body">
|
||
<canvas id="deptChart" height="240"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Confirm Modal (shared) -->
|
||
<div class="modal-overlay" id="confirmModal">
|
||
<div class="modal" style="max-width:420px">
|
||
<div class="modal-header"><span class="modal-title confirm-title">Confirm Action</span><button class="modal-close">✕</button></div>
|
||
<div class="modal-body"><p class="confirm-message" style="color:var(--text-secondary);font-size:13.5px"></p></div>
|
||
<div class="modal-footer"><button class="btn btn-ghost" onclick="closeModal('confirmModal')">Cancel</button><button class="btn btn-danger confirm-ok">Confirm</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toast-container" id="toastContainer"></div>
|
||
|
||
<script src="js/data.js"></script>
|
||
<script src="js/app.js"></script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
renderAlerts();
|
||
renderKPIs();
|
||
renderActivity();
|
||
renderActionItems();
|
||
initCharts();
|
||
});
|
||
|
||
function renderAlerts() {
|
||
const alerts = [
|
||
{ type:'warning', icon:'⚠️', title:'3 AMC contracts expiring within 30 days', text:'Cisco (Jun 30), HP Warranty (Aug 20), Epson (Nov 10). Renew before expiry to avoid service gaps.' },
|
||
{ type:'danger', icon:'🔴', title:'PM overdue: 2 scheduled maintenances not completed', text:'Daikin AC (Floor 2) and Ricoh Photocopier (Admin) are past due for preventive maintenance.' }
|
||
];
|
||
document.getElementById('alertsArea').innerHTML = alerts.map(a => `
|
||
<div class="alert alert-${a.type}">
|
||
<div class="alert-icon">${a.icon}</div>
|
||
<div><div class="alert-title">${a.title}</div><div class="alert-text">${a.text}</div></div>
|
||
<button onclick="this.closest('.alert').remove()" style="background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;margin-left:auto;padding:0">×</button>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function renderKPIs() {
|
||
const s = AMS.stats;
|
||
const kpis = [
|
||
{ label:'Total Assets', value: s.total.toLocaleString('en-IN'), icon:'📦', color:'var(--primary)', bg:'var(--primary-glow)', change:'+3.2%', up:true, link:'assets.html' },
|
||
{ label:'Active Assets', value: s.active.toLocaleString('en-IN'),icon:'✅', color:'var(--success)', bg:'var(--success-bg)', change:'+1.4%', up:true, link:'assets.html' },
|
||
{ label:'Net Book Value', value: fmt(s.netValue), icon:'💰', color:'var(--cyan)', bg:'var(--cyan-glow)', change:'SLM/WDV applied', up:true, link:'reports.html' },
|
||
{ label:'Pending Tickets', value: s.pendingTickets, icon:'🔧', color:'var(--warning)', bg:'var(--warning-bg)', change:'Needs attention', up:false,link:'maintenance.html' },
|
||
{ label:'Under Maintenance', value: s.maintenance, icon:'🛠️', color:'var(--info)', bg:'var(--info-bg)', change:'2 critical', up:false, link:'maintenance.html' },
|
||
{ label:'Idle Assets', value: s.idle, icon:'😴', color:'var(--warning)', bg:'var(--warning-bg)', change:'Review needed', up:false, link:'assets.html' },
|
||
{ label:'Pending PRs', value: s.pendingPRs, icon:'🛒', color:'var(--purple)', bg:'var(--purple-bg)', change:'Awaiting approval', up:false, link:'procurement.html' },
|
||
{ label:'Low Stock Alerts', value: s.lowStock, icon:'📉', color:'var(--danger)', bg:'var(--danger-bg)', change:'Reorder needed', up:false, link:'inventory.html' }
|
||
];
|
||
document.getElementById('kpiCards').innerHTML = kpis.map(k => `
|
||
<div class="stat-card" style="--sc-color:${k.color};cursor:pointer" onclick="window.location.href='${k.link}'">
|
||
<div class="stat-icon" style="background:${k.bg};color:${k.color}">${k.icon}</div>
|
||
<div class="stat-label">${k.label}</div>
|
||
<div class="stat-value" style="color:${k.color}">${k.value}</div>
|
||
<div class="stat-change ${k.up?'up':'down'}">${k.up?'↑':'↓'} ${k.change}</div>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function renderActivity() {
|
||
document.getElementById('activityFeed').innerHTML = AMS.activityFeed.map(a => `
|
||
<div class="activity-item">
|
||
<div class="act-av" style="background:${a.color}22;color:${a.color}">${a.av}</div>
|
||
<div style="flex:1">
|
||
<div class="act-text"><strong>${a.user}</strong> ${a.action} <strong>${a.target}</strong> ${a.detail}</div>
|
||
<div class="act-time">${a.time}</div>
|
||
</div>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function renderActionItems() {
|
||
const items = [
|
||
{ icon:'❄️', text:'Daikin AC service overdue by 13 days', link:'maintenance.html', priority:'danger' },
|
||
{ icon:'🛒', text:'8 Purchase Requests awaiting approval', link:'procurement.html', priority:'warning' },
|
||
{ icon:'📄', text:'AMC renewal: Cisco contract expires Jun 30', link:'maintenance.html', priority:'warning' },
|
||
{ icon:'🔋', text:'UPS batteries awaiting parts delivery', link:'maintenance.html', priority:'info' },
|
||
{ icon:'📋', text:'Physical audit Q2 not yet scheduled', link:'reports.html', priority:'info' },
|
||
{ icon:'🖨️', text:'Ricoh photocopier out of service (TKT-005)', link:'maintenance.html', priority:'danger' }
|
||
];
|
||
document.getElementById('actionItems').innerHTML = items.map(item => `
|
||
<div class="flex items-center gap-3 mb-3" style="cursor:pointer;padding:10px;background:var(--bg-surface);border-radius:var(--radius-md);border:1px solid var(--border);transition:var(--t)" onclick="window.location.href='${item.link}'" onmouseenter="this.style.borderColor='var(--${item.priority})'" onmouseleave="this.style.borderColor='var(--border)'">
|
||
<span style="font-size:18px">${item.icon}</span>
|
||
<span style="font-size:12.5px;color:var(--text-secondary);flex:1">${item.text}</span>
|
||
<span class="badge badge-${item.priority}" style="font-size:9px">${item.priority==='danger'?'Critical':item.priority==='warning'?'High':'Info'}</span>
|
||
</div>`).join('');
|
||
}
|
||
|
||
function initCharts() {
|
||
const gridColor = 'rgba(255,255,255,0.04)';
|
||
const tickColor = '#4B5563';
|
||
|
||
// Status Chart (Bar)
|
||
new Chart(document.getElementById('statusChart'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: ['Active','Idle','Maintenance','Disposed','Lost/Stolen'],
|
||
datasets: [{
|
||
label: 'Assets',
|
||
data: [1089, 63, 47, 48, 0],
|
||
backgroundColor: ['rgba(16,185,129,.75)','rgba(245,158,11,.75)','rgba(59,130,246,.75)','rgba(75,85,99,.75)','rgba(239,68,68,.75)'],
|
||
borderColor: ['#10B981','#F59E0B','#3B82F6','#4B5563','#EF4444'],
|
||
borderWidth: 1, borderRadius: 6
|
||
}]
|
||
},
|
||
options: {
|
||
responsive:true, maintainAspectRatio:false, plugins:{ legend:{display:false} },
|
||
scales:{
|
||
x:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11}}},
|
||
y:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11}}}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Category Doughnut
|
||
const cats = AMS.categories.slice(0,8);
|
||
new Chart(document.getElementById('categoryChart'), {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: cats.map(c=>c.name),
|
||
datasets:[{ data: cats.map(c=>c.count),
|
||
backgroundColor:['#6366F1','#06B6D4','#10B981','#F59E0B','#EF4444','#A855F7','#3B82F6','#14B8A6'],
|
||
borderColor: 'rgba(19,25,38,0)', borderWidth:2, hoverOffset:6
|
||
}]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false, cutout:'62%',
|
||
plugins:{ legend:{position:'right', labels:{color:'#94A3B8',font:{family:'Inter',size:11},padding:10,usePointStyle:true}} }
|
||
}
|
||
});
|
||
|
||
// Depreciation Line
|
||
const months = ['Oct','Nov','Dec','Jan','Feb','Mar','Apr','May'];
|
||
new Chart(document.getElementById('depreciationChart'), {
|
||
type: 'line',
|
||
data: {
|
||
labels: months,
|
||
datasets: [
|
||
{ label:'Gross Value', data:[45200000,45500000,45800000,46200000,46800000,47200000,47600000,48000000],
|
||
borderColor:'#6366F1', backgroundColor:'rgba(99,102,241,.08)', fill:true, tension:.4, pointRadius:3, borderWidth:2 },
|
||
{ label:'Net Book Value', data:[33200000,32800000,32300000,31900000,31400000,30900000,30500000,30100000],
|
||
borderColor:'#10B981', backgroundColor:'rgba(16,185,129,.07)', fill:true, tension:.4, pointRadius:3, borderWidth:2 }
|
||
]
|
||
},
|
||
options:{
|
||
responsive:true, maintainAspectRatio:false,
|
||
plugins:{ legend:{labels:{color:'#94A3B8',font:{family:'Inter',size:11},usePointStyle:true}} },
|
||
scales:{
|
||
x:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11}}},
|
||
y:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11},callback:v=>'₹'+(v/1e5).toFixed(0)+'L'}}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Department Horizontal Bar
|
||
const depts = AMS.departments;
|
||
new Chart(document.getElementById('deptChart'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: depts.map(d=>d.code),
|
||
datasets:[{
|
||
label:'Assets', data: depts.map(d=>d.assetCount),
|
||
backgroundColor:'rgba(99,102,241,.6)', borderColor:'#6366F1', borderWidth:1, borderRadius:5
|
||
}]
|
||
},
|
||
options:{
|
||
indexAxis:'y', responsive:true, maintainAspectRatio:false, plugins:{legend:{display:false}},
|
||
scales:{
|
||
x:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11}}},
|
||
y:{grid:{color:gridColor},ticks:{color:tickColor,font:{family:'Inter',size:11}}}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// Notification panel
|
||
function markAllRead() {
|
||
AMS.notifications.forEach(n=>n.read=true);
|
||
renderNotifPanel();
|
||
document.querySelector('.badge-dot').style.display='none';
|
||
showToast('All caught up!','All notifications marked as read','success');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|