marco.asseto.prototype/maintenance.html
Vaibhav Surve 2dfe150a5c feat: AMS V1 — multi-page HTML prototype
- 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
2026-05-28 18:09:32 +05:30

369 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Maintenance | AMS</title>
<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">
</head>
<body>
<div class="app-layout">
<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"><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</a>
<a href="procurement.html" class="nav-item"><span class="nav-icon">🛒</span> Procurement</a>
<div class="nav-section-label">Operations</div>
<a href="maintenance.html" class="nav-item active"><span class="nav-icon">🔧</span> Maintenance</a>
<a href="reports.html" class="nav-item"><span class="nav-icon">📈</span> Reports</a>
<div class="nav-section-label">Administration</div>
<a href="users.html" class="nav-item"><span class="nav-icon">👥</span> Users</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></div></div>
</aside>
<div class="main-wrapper">
<header class="topbar">
<div class="topbar-left"><div class="topbar-title">Maintenance Management</div></div>
<div class="topbar-actions">
<button class="btn btn-primary btn-sm" onclick="openModal('ticketModal')">+ Raise Ticket</button>
<a href="index.html" class="icon-btn">🚪</a>
</div>
</header>
<main class="content">
<!-- Stats -->
<div class="grid-4 mb-4" id="maintStats"></div>
<!-- Tabs -->
<div class="tab-container">
<div class="tabs" data-group="maint">
<button class="tab-btn active" data-tab="tab-kanban" data-group="maint">📋 Kanban Board</button>
<button class="tab-btn" data-tab="tab-list" data-group="maint">📄 All Tickets</button>
<button class="tab-btn" data-tab="tab-pm" data-group="maint">🗓️ PM Schedule <span class="badge badge-warning" style="margin-left:4px;font-size:9px">2 overdue</span></button>
<button class="tab-btn" data-tab="tab-amc" data-group="maint">📋 AMC &amp; Warranty</button>
</div>
<!-- Kanban -->
<div class="tab-content active" id="tab-kanban" data-group="maint">
<div class="kanban-board" id="kanbanBoard"></div>
</div>
<!-- All Tickets List -->
<div class="tab-content" id="tab-list" data-group="maint">
<div class="filters-row mb-3">
<div class="search-wrap" style="max-width:280px"><span style="color:var(--text-muted)">🔍</span><input type="text" id="ticketSearch" placeholder="Search tickets…"></div>
<select class="filter-sel" id="ticketPriorityFilter"><option value="">All Priorities</option><option>Critical</option><option>High</option><option>Medium</option><option>Low</option></select>
<select class="filter-sel" id="ticketStatusFilter"><option value="">All Statuses</option><option>Open</option><option>In Progress</option><option>Pending Parts</option><option>Resolved</option></select>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead><tr><th>Ticket ID</th><th>Issue</th><th>Asset</th><th>Priority</th><th>Status</th><th>Dept</th><th>Assigned To</th><th>Created</th><th>Actions</th></tr></thead>
<tbody id="ticketTbody"></tbody>
</table>
</div>
</div>
<!-- PM Schedule -->
<div class="tab-content" id="tab-pm" data-group="maint">
<div class="alert alert-warning mb-4">
<div class="alert-icon">⚠️</div>
<div><div class="alert-title">2 Preventive Maintenance tasks overdue!</div>
<div class="alert-text">Daikin AC (overdue 13 days) and Ricoh Photocopier (overdue 38 days). Schedule service immediately.</div></div>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead><tr><th>PM ID</th><th>Asset</th><th>Frequency</th><th>Last Done</th><th>Next Due</th><th>Status</th><th>Assigned To</th><th>Actions</th></tr></thead>
<tbody id="pmTbody"></tbody>
</table>
</div>
<div style="margin-top:16px">
<button class="btn btn-primary" onclick="openModal('pmModal')">+ Schedule Preventive Maintenance</button>
</div>
</div>
<!-- AMC Tab -->
<div class="tab-content" id="tab-amc" data-group="maint">
<div class="alert alert-warning mb-4">
<div class="alert-icon"></div>
<div><div class="alert-title">1 AMC contract expiring within 30 days</div><div class="alert-text">Cisco Network Infrastructure AMC expires June 30, 2025. Renewal must be initiated immediately.</div></div>
<button class="btn btn-warning btn-sm" style="margin-left:auto;flex-shrink:0;color:var(--warning);background:var(--warning-bg);border-color:rgba(245,158,11,.2)" onclick="openModal('amcModal')">Renew Now →</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead><tr><th>Contract ID</th><th>Vendor</th><th>Scope</th><th>Value</th><th>Valid From</th><th>Valid To</th><th>Status</th><th>Next Service</th><th>Actions</th></tr></thead>
<tbody id="amcTbody"></tbody>
</table>
</div>
<div style="margin-top:14px">
<button class="btn btn-primary" onclick="openModal('amcModal')">+ Add AMC Contract</button>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Ticket Modal -->
<div class="modal-overlay" id="ticketModal">
<div class="modal modal-lg">
<div class="modal-header"><span class="modal-title">Raise Maintenance Ticket</span><button class="modal-close"></button></div>
<div class="modal-body">
<div class="form-row">
<div class="form-group"><label class="form-label">Asset <span class="req">*</span></label>
<select class="form-select" id="ticketAsset"><option value="">Search asset…</option>
${window.AMS ? '' : ''}
</select>
</div>
<div class="form-group"><label class="form-label">Category <span class="req">*</span></label>
<select class="form-select"><option>Hardware Failure</option><option>Software Issue</option><option>Performance Issue</option><option>Electrical Issue</option><option>Mechanical Issue</option><option>Network Issue</option><option>Consumable Replacement</option><option>Preventive Check</option></select>
</div>
</div>
<div class="form-group"><label class="form-label">Issue Title <span class="req">*</span></label><input class="form-input" placeholder="Brief description of the issue"></div>
<div class="form-group"><label class="form-label">Detailed Description</label><textarea class="form-textarea" rows="3" placeholder="Steps to reproduce, error messages, symptoms…"></textarea></div>
<div class="form-row">
<div class="form-group"><label class="form-label">Priority <span class="req">*</span></label>
<select class="form-select">
<option>Critical System down, data loss risk</option>
<option>High Core workflow broken</option>
<option selected>Medium Significant but workaround exists</option>
<option>Low Cosmetic / minor issue</option>
</select>
</div>
<div class="form-group"><label class="form-label">Assign To</label>
<select class="form-select"><option>Kavya Nair (IT)</option><option>External Vendor</option><option>Dell Support</option><option>Daikin AMC Team</option><option>Ricoh Service</option><option>Auto-assign</option></select>
</div>
</div>
<div class="form-group"><label class="form-label">Attachments (photos, logs)</label><input type="file" class="form-input" multiple accept=".pdf,.jpg,.png,.log"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('ticketModal')">Cancel</button>
<button class="btn btn-primary" onclick="saveTicket()">Create Ticket</button>
</div>
</div>
</div>
<!-- PM Modal -->
<div class="modal-overlay" id="pmModal">
<div class="modal">
<div class="modal-header"><span class="modal-title">Schedule PM</span><button class="modal-close"></button></div>
<div class="modal-body">
<div class="form-group"><label class="form-label">Asset <span class="req">*</span></label><select class="form-select"><option>Daikin 1.5T Split AC (AST-2025-005)</option><option>Dell PowerEdge R740 (AST-2025-004)</option><option>Honda City (AST-2025-014)</option><option>APC Smart UPS 3KVA (AST-2025-012)</option></select></div>
<div class="form-row">
<div class="form-group"><label class="form-label">Frequency</label><select class="form-select"><option>Monthly</option><option>Quarterly</option><option>Half-Yearly</option><option>Yearly</option></select></div>
<div class="form-group"><label class="form-label">Assigned To</label><input class="form-input" placeholder="Technician or vendor"></div>
</div>
<div class="form-group"><label class="form-label">First PM Date <span class="req">*</span></label><input type="date" class="form-input"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('pmModal')">Cancel</button>
<button class="btn btn-primary" onclick="savePM()">Schedule PM</button>
</div>
</div>
</div>
<!-- AMC Modal -->
<div class="modal-overlay" id="amcModal">
<div class="modal modal-lg">
<div class="modal-header"><span class="modal-title">AMC Contract</span><button class="modal-close"></button></div>
<div class="modal-body">
<div class="form-row">
<div class="form-group"><label class="form-label">Vendor <span class="req">*</span></label><select class="form-select"><option>Daikin Aircon India</option><option>Dell India Pvt. Ltd.</option><option>Cisco Systems India</option><option>Godrej Security</option></select></div>
<div class="form-group"><label class="form-label">Contract Value (₹) <span class="req">*</span></label><input type="number" class="form-input" placeholder="Annual value"></div>
</div>
<div class="form-group"><label class="form-label">Scope of Work</label><textarea class="form-textarea" rows="2" placeholder="What assets/services are covered"></textarea></div>
<div class="form-row">
<div class="form-group"><label class="form-label">Valid From</label><input type="date" class="form-input"></div>
<div class="form-group"><label class="form-label">Valid To</label><input type="date" class="form-input"></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('amcModal')">Cancel</button>
<button class="btn btn-primary" onclick="saveAMC()">Save Contract</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', () => {
renderStats();
renderKanban();
renderTicketList();
renderPMSchedule();
renderAMC();
initTabs();
// Populate asset select in ticket modal
const sel = document.getElementById('ticketAsset');
AMS.assets.forEach(a => { const o=document.createElement('option'); o.value=a.id; o.textContent=`${a.name} (${a.id})`; sel.appendChild(o); });
document.getElementById('ticketSearch').addEventListener('input', filterTickets);
document.getElementById('ticketPriorityFilter').addEventListener('change', filterTickets);
document.getElementById('ticketStatusFilter').addEventListener('change', filterTickets);
});
function renderStats() {
const t = AMS.tickets;
document.getElementById('maintStats').innerHTML = [
{ label:'Open Tickets', icon:'🔴', val:t.filter(x=>x.status==='Open').length, color:'var(--danger)' },
{ label:'In Progress', icon:'🔵', val:t.filter(x=>x.status==='In Progress').length, color:'var(--info)' },
{ label:'Pending Parts', icon:'⏳', val:t.filter(x=>x.status==='Pending Parts').length, color:'var(--warning)' },
{ label:'Resolved (30d)', icon:'✅', val:t.filter(x=>x.status==='Resolved').length, color:'var(--success)' }
].map(s=>`<div class="stat-card" style="--sc-color:${s.color}"><div class="stat-icon" style="background:${s.color}18;color:${s.color}">${s.icon}</div><div class="stat-label">${s.label}</div><div class="stat-value" style="color:${s.color}">${s.val}</div></div>`).join('');
}
const kanbanCols = [
{ id:'Open', title:'Open', color:'var(--danger)', count:0 },
{ id:'In Progress', title:'In Progress', color:'var(--info)', count:0 },
{ id:'Pending Parts', title:'Pending Parts', color:'var(--warning)', count:0 },
{ id:'Resolved', title:'Resolved', color:'var(--success)', count:0 },
{ id:'Closed', title:'Closed', color:'var(--text-muted)', count:0 }
];
function renderKanban() {
const board = document.getElementById('kanbanBoard');
board.innerHTML = kanbanCols.map(col => {
const colTickets = AMS.tickets.filter(t => t.status === col.id);
const cards = colTickets.map(t => `
<div class="kanban-card" onclick="openTicketDetail('${t.id}')">
<div class="kanban-card-title">${t.title || t.name}</div>
<div style="font-size:11px;color:var(--text-muted);margin-bottom:8px">${t.assetName}</div>
<div class="kanban-meta">
<span class="badge ${statusBadge(t.priority)}" style="font-size:9px">${t.priority}</span>
<span style="font-size:10.5px;color:var(--text-muted)">${t.assignedTo}</span>
</div>
<div style="font-size:10px;color:var(--text-muted);margin-top:6px">🗓️ ${fmtDate(t.created)}</div>
</div>`).join('') || `<div class="empty-state" style="padding:20px"><div class="empty-icon" style="font-size:28px">✅</div><div style="font-size:12px;color:var(--text-muted)">No tickets</div></div>`;
return `
<div class="kanban-col">
<div class="kanban-col-header">
<span class="kanban-col-title"><span style="width:8px;height:8px;border-radius:50%;background:${col.color};display:inline-block;flex-shrink:0"></span>${col.title}</span>
<span class="kanban-count">${colTickets.length}</span>
</div>
<div class="kanban-cards">${cards}</div>
${col.id==='Open'?`<div style="padding:8px 10px 10px"><button class="btn btn-ghost btn-sm w-full" onclick="openModal('ticketModal')">+ Add Ticket</button></div>`:''}
</div>`;
}).join('');
}
function renderTicketList(data) {
const tickets = data || AMS.tickets;
document.getElementById('ticketTbody').innerHTML = tickets.map(t => `
<tr onclick="openTicketDetail('${t.id}')">
<td><code style="font-size:11.5px;color:var(--primary-light)">${t.id}</code></td>
<td><div style="font-weight:600;color:var(--text-primary)">${t.title||t.name}</div><div style="font-size:11px;color:var(--text-muted)">${t.category}</div></td>
<td><div style="font-size:12.5px">${t.assetName}</div><div style="font-size:11px;color:var(--text-muted)">${t.asset}</div></td>
<td><span class="badge ${statusBadge(t.priority)}">${t.priority}</span></td>
<td><span class="badge ${statusBadge(t.status)}">${t.status}</span></td>
<td>${t.dept}</td>
<td>${t.assignedTo}</td>
<td>${fmtDate(t.created)}</td>
<td onclick="event.stopPropagation()">
<div class="flex gap-1">
<button class="btn btn-ghost btn-sm" onclick="updateStatus('${t.id}')">Update</button>
<button class="btn btn-ghost btn-sm" onclick="resolveTicket('${t.id}')">✓ Resolve</button>
</div>
</td>
</tr>`).join('');
}
function filterTickets() {
const q = document.getElementById('ticketSearch').value.toLowerCase();
const pr = document.getElementById('ticketPriorityFilter').value;
const st = document.getElementById('ticketStatusFilter').value;
const filtered = AMS.tickets.filter(t =>
(!q || (t.title||t.name||'').toLowerCase().includes(q) || t.assetName.toLowerCase().includes(q)) &&
(!pr || t.priority === pr) &&
(!st || t.status === st)
);
renderTicketList(filtered);
}
function renderPMSchedule() {
document.getElementById('pmTbody').innerHTML = AMS.pmSchedule.map(pm => `
<tr>
<td><code style="font-size:11px;color:var(--primary-light)">${pm.id}</code></td>
<td><div style="font-weight:600">${pm.asset}</div><div style="font-size:11px;color:var(--text-muted)">${pm.assetId}</div></td>
<td>${pm.freq}</td>
<td>${fmtDate(pm.lastDone)}</td>
<td style="font-weight:600;color:${pm.status==='Overdue'?'var(--danger)':pm.status==='Due Today'?'var(--warning)':'var(--text-primary)'}">${fmtDate(pm.nextDue)}</td>
<td><span class="badge ${statusBadge(pm.status)}">${pm.status}</span></td>
<td>${pm.assignee}</td>
<td>
<button class="btn btn-secondary btn-sm" onclick="markPMDone('${pm.id}')">✓ Mark Done</button>
<button class="btn btn-ghost btn-sm" onclick="showToast('Reschedule','PM rescheduling dialog','info')">Reschedule</button>
</td>
</tr>`).join('');
}
function renderAMC() {
document.getElementById('amcTbody').innerHTML = AMS.amcContracts.map(c => `
<tr>
<td><code style="font-size:11px;color:var(--primary-light)">${c.id}</code></td>
<td style="font-weight:600">${c.vendor}</td>
<td style="font-size:12px;max-width:200px;color:var(--text-secondary)">${c.scope}</td>
<td style="font-weight:700">${fmt(c.value)}</td>
<td>${fmtDate(c.start)}</td>
<td style="font-weight:600;color:${c.status==='Expiring'?'var(--warning)':'var(--text-primary)'}">${fmtDate(c.end)}</td>
<td><span class="badge ${c.status==='Expiring'?'badge-warning':'badge-success'}">${c.status}</span></td>
<td>${fmtDate(c.nextService)}</td>
<td>
${c.status==='Expiring'?`<button class="btn btn-warning btn-sm" style="color:var(--warning);background:var(--warning-bg)" onclick="renewAMC('${c.id}')">Renew</button>`:
`<button class="btn btn-ghost btn-sm" onclick="showToast('View','Opening AMC document','info')">View</button>`}
</td>
</tr>`).join('');
}
function openTicketDetail(id) {
const t = AMS.tickets.find(x => x.id === id);
if (!t) return;
showToast(t.id, `${t.title||t.name}${t.status}`, 'info');
}
function updateStatus(id) {
const t = AMS.tickets.find(x=>x.id===id);
if (!t) return;
const statuses = ['Open','In Progress','Pending Parts','Resolved','Closed'];
const curr = statuses.indexOf(t.status);
t.status = statuses[Math.min(curr+1, statuses.length-1)];
renderKanban(); renderTicketList();
showToast('Status Updated',`${id}${t.status}`,'success');
}
function resolveTicket(id) {
const t = AMS.tickets.find(x=>x.id===id);
if(t) t.status='Resolved';
renderKanban(); renderTicketList();
showToast('Ticket Resolved',`${id} marked as resolved`,'success');
}
function markPMDone(id) {
const pm = AMS.pmSchedule.find(x=>x.id===id);
if(pm) { pm.lastDone='2025-05-28'; pm.status='Upcoming'; }
renderPMSchedule();
showToast('PM Completed',`${id} marked as done. Next due auto-calculated.`,'success');
}
function renewAMC(id) {
const c = AMS.amcContracts.find(x=>x.id===id);
if(c) c.status='Active';
renderAMC();
showToast('AMC Renewed','Contract renewed for another year','success');
}
function saveTicket() { closeModal('ticketModal'); showToast('Ticket Created','Assigned to technician, email notification sent','success'); }
function savePM() { closeModal('pmModal'); showToast('PM Scheduled','Preventive maintenance schedule created','success'); }
function saveAMC() { closeModal('amcModal'); showToast('AMC Added','Contract added and team notified','success'); }
</script>
</body>
</html>