421 lines
22 KiB
HTML
421 lines
22 KiB
HTML
<!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" id="appSidebar"></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" title="Logout"><i data-lucide="log-out"></i></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 & 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)"><i data-lucide="search" style="width:14px;height:14px"></i></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></select>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">Category <span class="req">*</span></label>
|
||
<select class="form-select" id="ticketCategory"><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" id="ticketTitle" 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" id="ticketPriority">
|
||
<option value="Critical">Critical – System down, data loss risk</option>
|
||
<option value="High">High – Core workflow broken</option>
|
||
<option value="Medium" selected>Medium – Significant but workaround exists</option>
|
||
<option value="Low">Low – Cosmetic / minor issue</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">Assign To</label>
|
||
<select class="form-select" id="ticketAssignee"><option>Kavya Nair</option><option>External Vendor</option><option>Dell Support</option><option>Daikin AMC Team</option><option>Ricoh Service</option><option>Unassigned</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" id="pmAsset"><option value="AST-2025-005">Daikin 1.5T Split AC (AST-2025-005)</option><option value="AST-2025-004">Dell PowerEdge R740 (AST-2025-004)</option><option value="AST-2025-014">Honda City (AST-2025-014)</option><option value="AST-2025-012">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" id="pmFreq"><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" id="pmAssignee" 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" id="pmDate"></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" id="amcVendor"><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" id="amcValue" placeholder="Annual value"></div>
|
||
</div>
|
||
<div class="form-group"><label class="form-label">Scope of Work</label><textarea class="form-textarea" id="amcScope" 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" id="amcStart"></div>
|
||
<div class="form-group"><label class="form-label">Valid To</label><input type="date" class="form-input" id="amcEnd"></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="https://unpkg.com/lucide@latest"></script>
|
||
<script src="js/data.js"></script>
|
||
<script src="js/sidebar.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">${escapeHtml(t.title || t.name)}</div>
|
||
<div style="font-size:11px;color:var(--text-muted);margin-bottom:8px">${escapeHtml(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)">${escapeHtml(t.id)}</code></td>
|
||
<td><div style="font-weight:600;color:var(--text-primary)">${escapeHtml(t.title||t.name)}</div><div style="font-size:11px;color:var(--text-muted)">${escapeHtml(t.category)}</div></td>
|
||
<td><div style="font-size:12.5px">${escapeHtml(t.assetName)}</div><div style="font-size:11px;color:var(--text-muted)">${escapeHtml(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)];
|
||
AMS.save();
|
||
renderStats(); 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';
|
||
AMS.save();
|
||
renderStats(); renderKanban(); renderTicketList();
|
||
showToast('Ticket Resolved',`${id} marked as resolved`,'success');
|
||
}
|
||
|
||
function todayStr() { return new Date().toISOString().slice(0,10); }
|
||
|
||
function markPMDone(id) {
|
||
const pm = AMS.pmSchedule.find(x=>x.id===id);
|
||
if(pm) { pm.lastDone=todayStr(); pm.status='Upcoming'; }
|
||
AMS.save();
|
||
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';
|
||
AMS.save();
|
||
renderAMC();
|
||
showToast('AMC Renewed','Contract renewed for another year','success');
|
||
}
|
||
|
||
function nextSeqId(arr, prefix) {
|
||
const n = arr.reduce((m,x)=>{ const v=parseInt(String(x.id).split('-').pop(),10); return isNaN(v)?m:Math.max(m,v); },0)+1;
|
||
return `${prefix}-${String(n).padStart(3,'0')}`;
|
||
}
|
||
|
||
function saveTicket() {
|
||
const title = document.getElementById('ticketTitle').value.trim();
|
||
const assetId = document.getElementById('ticketAsset').value;
|
||
if(!title || !assetId) { showToast('Missing Details','Asset and issue title are required','error'); return; }
|
||
const asset = AMS.assets.find(a=>a.id===assetId);
|
||
AMS.tickets.unshift({
|
||
id: nextSeqId(AMS.tickets,'TKT'),
|
||
title,
|
||
asset: assetId,
|
||
assetName: asset ? asset.name : assetId,
|
||
priority: document.getElementById('ticketPriority').value,
|
||
status: 'Open',
|
||
assignedTo: document.getElementById('ticketAssignee').value,
|
||
created: todayStr(),
|
||
dept: asset ? asset.dept : '',
|
||
category: document.getElementById('ticketCategory').value
|
||
});
|
||
AMS.save();
|
||
renderStats(); renderKanban(); renderTicketList();
|
||
closeModal('ticketModal');
|
||
document.getElementById('ticketTitle').value = '';
|
||
showToast('Ticket Created','Assigned to technician, email notification sent','success');
|
||
}
|
||
|
||
function savePM() {
|
||
const assetId = document.getElementById('pmAsset').value;
|
||
const date = document.getElementById('pmDate').value;
|
||
if(!date) { showToast('Missing Details','First PM date is required','error'); return; }
|
||
const asset = AMS.assets.find(a=>a.id===assetId);
|
||
AMS.pmSchedule.push({
|
||
id: nextSeqId(AMS.pmSchedule,'PM'),
|
||
asset: asset ? asset.name : assetId,
|
||
assetId,
|
||
freq: document.getElementById('pmFreq').value,
|
||
lastDone: '—',
|
||
nextDue: date,
|
||
status: 'Upcoming',
|
||
assignee: document.getElementById('pmAssignee').value.trim() || 'Unassigned'
|
||
});
|
||
AMS.save();
|
||
renderPMSchedule();
|
||
closeModal('pmModal');
|
||
showToast('PM Scheduled','Preventive maintenance schedule created','success');
|
||
}
|
||
|
||
function saveAMC() {
|
||
const value = parseInt(document.getElementById('amcValue').value,10);
|
||
if(!value) { showToast('Missing Details','Contract value is required','error'); return; }
|
||
AMS.amcContracts.push({
|
||
id: nextSeqId(AMS.amcContracts,'AMC'),
|
||
vendor: document.getElementById('amcVendor').value,
|
||
scope: document.getElementById('amcScope').value.trim(),
|
||
value,
|
||
start: document.getElementById('amcStart').value || todayStr(),
|
||
end: document.getElementById('amcEnd').value || '',
|
||
status: 'Active',
|
||
nextService: document.getElementById('amcStart').value || todayStr()
|
||
});
|
||
AMS.save();
|
||
renderAMC();
|
||
closeModal('amcModal');
|
||
showToast('AMC Added','Contract added and team notified','success');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|