marco.asseto.prototype/procurement.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

370 lines
22 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>Procurement | 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 active"><span class="nav-icon">🛒</span> Procurement</a>
<div class="nav-section-label">Operations</div>
<a href="maintenance.html" class="nav-item"><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">Procurement <span class="topbar-sub">PR → Approval → PO → GRN → Asset</span></div></div>
<div class="topbar-actions">
<button class="btn btn-primary btn-sm" onclick="openModal('prModal')">+ New Purchase Request</button>
<a href="index.html" class="icon-btn">🚪</a>
</div>
</header>
<main class="content">
<!-- Workflow Visualization -->
<div class="card mb-4">
<div class="card-body">
<div style="font-size:11px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.6px;margin-bottom:12px">Procurement Workflow</div>
<div class="wf-steps">
<div class="wf-step done">✅ PR Raised</div><div class="wf-arr"></div>
<div class="wf-step done">✅ Dept Approval</div><div class="wf-arr"></div>
<div class="wf-step active">⏳ Finance Approval</div><div class="wf-arr"></div>
<div class="wf-step">📋 PO Generated</div><div class="wf-arr"></div>
<div class="wf-step">📦 GRN</div><div class="wf-arr"></div>
<div class="wf-step">🧾 Invoice Match</div><div class="wf-arr"></div>
<div class="wf-step">🏷️ Asset Created</div>
</div>
</div>
</div>
<!-- Stats -->
<div class="grid-4 mb-4" id="procStats"></div>
<!-- Tabs -->
<div class="tab-container">
<div class="tabs" data-group="proc">
<button class="tab-btn active" data-tab="tab-prs" data-group="proc">📋 Purchase Requests <span class="badge badge-danger" style="margin-left:4px;font-size:9px">8</span></button>
<button class="tab-btn" data-tab="tab-pos" data-group="proc">📄 Purchase Orders</button>
<button class="tab-btn" data-tab="tab-grn" data-group="proc">📦 GRN</button>
<button class="tab-btn" data-tab="tab-vendors" data-group="proc">🏭 Vendors</button>
</div>
<!-- PRs Tab -->
<div class="tab-content active" id="tab-prs" data-group="proc">
<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="prSearch" placeholder="Search PRs…"></div>
<select class="filter-sel" id="prStatusFilter">
<option value="">All Statuses</option>
<option>Draft</option><option>Submitted</option><option>Approved</option><option>Rejected</option><option>PO Raised</option>
</select>
<select class="filter-sel"><option>All Departments</option><option>IT</option><option>Finance</option><option>HR</option><option>Operations</option><option>Marketing</option></select>
</div>
<div class="table-wrapper">
<table class="data-table" id="prTable">
<thead><tr><th>PR No.</th><th>Item Description</th><th>Dept</th><th>Qty</th><th>Est. Cost</th><th>Requester</th><th>Date</th><th>Status</th><th>Actions</th></tr></thead>
<tbody id="prTbody"></tbody>
</table>
</div>
</div>
<!-- POs Tab -->
<div class="tab-content" id="tab-pos" data-group="proc">
<div class="table-wrapper">
<table class="data-table">
<thead><tr><th>PO Number</th><th>Vendor</th><th>Items</th><th>Total Value</th><th>Date</th><th>Status</th><th>GRN Status</th><th>Actions</th></tr></thead>
<tbody id="poTbody"></tbody>
</table>
</div>
</div>
<!-- GRN Tab -->
<div class="tab-content" id="tab-grn" data-group="proc">
<div class="table-wrapper">
<table class="data-table">
<thead><tr><th>GRN No.</th><th>PO Ref.</th><th>Vendor</th><th>Items</th><th>Received Qty</th><th>Date</th><th>Received By</th><th>3-Way Match</th></tr></thead>
<tbody id="grnTbody"></tbody>
</table>
</div>
</div>
<!-- Vendors Tab -->
<div class="tab-content" id="tab-vendors" data-group="proc">
<div class="flex justify-between mb-4">
<div class="search-wrap" style="max-width:280px"><span style="color:var(--text-muted)">🔍</span><input type="text" placeholder="Search vendors…"></div>
<button class="btn btn-primary btn-sm" onclick="openModal('vendorModal')">+ Add Vendor</button>
</div>
<div class="grid-3" id="vendorGrid"></div>
</div>
</div>
</main>
</div>
</div>
<!-- PR Modal -->
<div class="modal-overlay" id="prModal">
<div class="modal modal-lg">
<div class="modal-header"><span class="modal-title">New Purchase Request</span><button class="modal-close"></button></div>
<div class="modal-body">
<div class="form-row">
<div class="form-group"><label class="form-label">PR Number</label><input class="form-input" value="PR-2025-009" readonly></div>
<div class="form-group"><label class="form-label">Department <span class="req">*</span></label><select class="form-select"><option>IT</option><option>Finance</option><option>HR</option><option>Operations</option><option>Marketing</option><option>Admin</option></select></div>
</div>
<div class="form-group"><label class="form-label">Item Description <span class="req">*</span></label><input class="form-input" placeholder="e.g. Dell Laptop i7 Gen 13 (×5)"></div>
<div class="form-row">
<div class="form-group"><label class="form-label">Quantity <span class="req">*</span></label><input type="number" class="form-input" placeholder="0" min="1"></div>
<div class="form-group"><label class="form-label">Estimated Unit Cost (₹) <span class="req">*</span></label><input type="number" class="form-input" placeholder="0.00"></div>
</div>
<div class="form-row">
<div class="form-group"><label class="form-label">Budget Code</label><input class="form-input" placeholder="CC-IT-001"></div>
<div class="form-group"><label class="form-label">Required By Date</label><input type="date" class="form-input"></div>
</div>
<div class="form-group"><label class="form-label">Business Justification <span class="req">*</span></label><textarea class="form-textarea" placeholder="Why is this purchase needed? Link to business objective or ticket…" rows="3"></textarea></div>
<div class="form-group"><label class="form-label">Attachments</label><input type="file" class="form-input" multiple accept=".pdf,.doc,.jpg,.png"></div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('prModal')">Cancel</button>
<button class="btn btn-secondary" onclick="savePR('Draft')">Save Draft</button>
<button class="btn btn-primary" onclick="savePR('Submitted')">Submit for Approval</button>
</div>
</div>
</div>
<!-- Vendor Modal -->
<div class="modal-overlay" id="vendorModal">
<div class="modal modal-lg">
<div class="modal-header"><span class="modal-title">Add Vendor</span><button class="modal-close"></button></div>
<div class="modal-body">
<div class="form-row">
<div class="form-group"><label class="form-label">Company Name <span class="req">*</span></label><input class="form-input" placeholder="Vendor company name"></div>
<div class="form-group"><label class="form-label">Contact Person</label><input class="form-input" placeholder="Primary contact name"></div>
</div>
<div class="form-row">
<div class="form-group"><label class="form-label">Email</label><input type="email" class="form-input" placeholder="vendor@company.com"></div>
<div class="form-group"><label class="form-label">Phone</label><input class="form-input" placeholder="+91-XXXXX-XXXXX"></div>
</div>
<div class="form-row">
<div class="form-group"><label class="form-label">GST Number</label><input class="form-input" placeholder="29XXXXXXXXXX1ZX"></div>
<div class="form-group"><label class="form-label">Category</label><select class="form-select"><option>IT Hardware</option><option>Networking</option><option>Furniture</option><option>HVAC</option><option>Vehicles</option><option>AV Equipment</option><option>Other</option></select></div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-ghost" onclick="closeModal('vendorModal')">Cancel</button>
<button class="btn btn-primary" onclick="saveVendor()">Add Vendor</button>
</div>
</div>
</div>
<!-- PR Detail Modal -->
<div class="modal-overlay" id="prDetailModal">
<div class="modal modal-lg">
<div class="modal-header"><span class="modal-title" id="prDetailTitle">PR Details</span><button class="modal-close"></button></div>
<div class="modal-body" id="prDetailBody"></div>
<div class="modal-footer" id="prDetailFooter"></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();
renderPRs();
renderPOs();
renderGRNs();
renderVendors();
initTabs();
document.getElementById('prSearch').addEventListener('input', applyPRFilter);
document.getElementById('prStatusFilter').addEventListener('change', applyPRFilter);
});
function renderStats() {
const prs = AMS.purchaseRequests;
const pending = prs.filter(p=>p.status==='Submitted'||p.status==='Pending').length;
const approved = prs.filter(p=>p.status==='Approved'||p.status==='PO Raised').length;
const totalVal = prs.filter(p=>p.status!=='Rejected').reduce((a,p)=>a+p.estCost,0);
document.getElementById('procStats').innerHTML = [
{label:'Total PRs',icon:'📋',val:prs.length,color:'var(--primary)'},
{label:'Pending Approval',icon:'⏳',val:pending,color:'var(--warning)'},
{label:'Approved / PO Raised',icon:'✅',val:approved,color:'var(--success)'},
{label:'Total Value',icon:'💰',val:fmt(totalVal),color:'var(--cyan)'}
].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('');
}
function renderPRs(data) {
const prs = data || AMS.purchaseRequests;
document.getElementById('prTbody').innerHTML = prs.map(p=>`
<tr onclick="showPRDetail('${p.id}')" style="cursor:pointer">
<td><code style="color:var(--primary-light);font-size:11.5px">${p.id}</code></td>
<td><div style="font-weight:600;color:var(--text-primary)">${p.item}</div><div style="font-size:11px;color:var(--text-muted)">${p.justification.substring(0,50)}…</div></td>
<td><span class="badge badge-neutral">${p.dept}</span></td>
<td style="font-weight:700">${p.qty}</td>
<td style="font-weight:700">${fmt(p.estCost)}</td>
<td>${p.requester}</td>
<td>${fmtDate(p.date)}</td>
<td><span class="badge ${statusBadge(p.status)}">${p.status}</span></td>
<td onclick="event.stopPropagation()">
<div class="flex gap-1">
${p.status==='Submitted'||p.status==='Pending' ? `
<button class="btn btn-success btn-sm" onclick="approvePR('${p.id}')">✓</button>
<button class="btn btn-danger btn-sm" onclick="rejectPR('${p.id}')">✕</button>` :
p.status==='Approved' ? `<button class="btn btn-primary btn-sm" onclick="raisePO('${p.id}')">PO →</button>` :
`<button class="btn btn-ghost btn-sm" onclick="showPRDetail('${p.id}')">View</button>`
}
</div>
</td>
</tr>`).join('');
}
function applyPRFilter() {
const q = document.getElementById('prSearch').value.toLowerCase();
const st = document.getElementById('prStatusFilter').value;
const filtered = AMS.purchaseRequests.filter(p =>
(!q || p.item.toLowerCase().includes(q) || p.id.toLowerCase().includes(q) || p.requester.toLowerCase().includes(q)) &&
(!st || p.status === st)
);
renderPRs(filtered);
}
function showPRDetail(id) {
const p = AMS.purchaseRequests.find(x=>x.id===id);
if(!p) return;
document.getElementById('prDetailTitle').textContent = p.id + ' ' + p.item;
document.getElementById('prDetailBody').innerHTML = `
<div class="info-grid mb-4">
<div class="info-item"><div class="info-label">Status</div><div class="info-value"><span class="badge ${statusBadge(p.status)}">${p.status}</span></div></div>
<div class="info-item"><div class="info-label">Department</div><div class="info-value">${p.dept}</div></div>
<div class="info-item"><div class="info-label">Requester</div><div class="info-value">${p.requester}</div></div>
<div class="info-item"><div class="info-label">Date</div><div class="info-value">${fmtDate(p.date)}</div></div>
<div class="info-item"><div class="info-label">Quantity</div><div class="info-value">${p.qty}</div></div>
<div class="info-item"><div class="info-label">Estimated Cost</div><div class="info-value" style="font-weight:700">${fmt(p.estCost)}</div></div>
</div>
<div class="form-group"><div class="info-label">Business Justification</div><div class="info-value" style="background:var(--bg-surface);padding:12px;border-radius:var(--radius-md);font-size:13px;margin-top:4px">${p.justification}</div></div>
${p.poRef ? `<div class="alert alert-success mt-4"><div class="alert-icon">✅</div><div><div class="alert-title">PO Generated</div><div class="alert-text">Reference: ${p.poRef}</div></div></div>` : ''}
<div class="section-hdr mt-4">Approval Chain</div>
<div class="timeline">
<div class="timeline-item"><div class="tl-icon-wrap"><div class="tl-icon" style="background:var(--success-bg);color:var(--success)">✅</div><div class="tl-line"></div></div><div class="tl-content"><div class="tl-title">Requester: ${p.requester}</div><div class="tl-desc">PR Submitted</div><div class="tl-time">${fmtDate(p.date)}</div></div></div>
<div class="timeline-item"><div class="tl-icon-wrap"><div class="tl-icon" style="background:${p.status!=='Draft'?'var(--success-bg)':'var(--bg-elevated)'};color:${p.status!=='Draft'?'var(--success)':'var(--text-muted)'}">${p.status!=='Draft'?'✅':'⏳'}</div><div class="tl-line"></div></div><div class="tl-content"><div class="tl-title">Dept Head Approval</div><div class="tl-desc">${p.dept} Head Review</div></div></div>
<div class="timeline-item"><div class="tl-icon-wrap"><div class="tl-icon" style="background:${['Approved','PO Raised','GRN Done'].includes(p.status)?'var(--success-bg)':'var(--bg-elevated)'};color:${['Approved','PO Raised','GRN Done'].includes(p.status)?'var(--success)':'var(--text-muted)'}">${['Approved','PO Raised','GRN Done'].includes(p.status)?'✅':'⏳'}</div></div><div class="tl-content"><div class="tl-title">Finance Approval</div><div class="tl-desc">Budget verification</div></div></div>
</div>`;
document.getElementById('prDetailFooter').innerHTML = p.status==='Submitted' ? `
<button class="btn btn-ghost" onclick="closeModal('prDetailModal')">Close</button>
<button class="btn btn-danger" onclick="rejectPR('${p.id}');closeModal('prDetailModal')">Reject</button>
<button class="btn btn-primary" onclick="approvePR('${p.id}');closeModal('prDetailModal')">Approve PR</button>` :
`<button class="btn btn-ghost" onclick="closeModal('prDetailModal')">Close</button>
${p.status==='Approved'?`<button class="btn btn-primary" onclick="raisePO('${p.id}');closeModal('prDetailModal')">Generate PO →</button>`:''}`;
openModal('prDetailModal');
}
function approvePR(id) {
const p = AMS.purchaseRequests.find(x=>x.id===id);
if(p) p.status = 'Approved';
renderPRs();
showToast('PR Approved',`${id} approved. Now raise a PO.`,'success');
}
function rejectPR(id) {
const p = AMS.purchaseRequests.find(x=>x.id===id);
if(p) p.status = 'Rejected';
renderPRs();
showToast('PR Rejected',`${id} has been rejected`,'warning');
}
function raisePO(prId) {
const p = AMS.purchaseRequests.find(x=>x.id===prId);
if(p) { p.status = 'PO Raised'; p.poRef = 'PO-2025-00' + Math.floor(Math.random()*9+1); }
renderPRs();
showToast('PO Generated',`Purchase Order ${p?.poRef} sent to vendor`,'success');
}
function renderPOs() {
const pos = [
{ id:'PO-2025-001', vendor:'Dell India Pvt. Ltd.', items:'5× Dell Latitude 5540', value:425000, date:'2025-05-12', status:'GRN Done', grn:'GRN-2025-041' },
{ id:'PO-2025-002', vendor:'TP-Link India', items:'8× WAP EAP670', value:96000, date:'2025-05-19', status:'PO Sent', grn:null },
{ id:'PO-2025-003', vendor:'Schneider Electric', items:'1× UPS Battery Set', value:18000, date:'2025-05-28', status:'PO Sent', grn:null }
];
document.getElementById('poTbody').innerHTML = pos.map(p=>`
<tr>
<td><code style="color:var(--primary-light);font-size:11.5px">${p.id}</code></td>
<td>${p.vendor}</td>
<td>${p.items}</td>
<td style="font-weight:700">${fmt(p.value)}</td>
<td>${fmtDate(p.date)}</td>
<td><span class="badge ${statusBadge(p.status)}">${p.status}</span></td>
<td>${p.grn ? `<code style="font-size:11px;color:var(--success)">${p.grn}</code>` : '<span class="text-muted">Pending</span>'}</td>
<td>
<button class="btn btn-ghost btn-sm" onclick="showToast('Download','PO PDF downloaded','info')">📥 PDF</button>
${!p.grn?`<button class="btn btn-secondary btn-sm" onclick="showToast('GRN','Record GRN for this PO','info')">Record GRN</button>`:''}
</td>
</tr>`).join('');
}
function renderGRNs() {
const grns = [
{ id:'GRN-2025-041', po:'PO-2025-001', vendor:'Dell India', items:'Dell Latitude 5540', qty:'5/5', date:'2025-05-14', by:'Arjun Sharma', match:'✅ Matched' },
{ id:'GRN-2025-040', po:'PO-2024-098', vendor:'Cisco Systems', items:'Cisco IP Phone 8841', qty:'5/5', date:'2025-05-22', by:'Kavya Nair', match:'✅ Matched' },
{ id:'GRN-2025-039', po:'PO-2024-095', vendor:'HP India', items:'HP LaserJet Pro M404', qty:'2/2', date:'2025-05-18', by:'Arjun Sharma', match:'⚠️ Invoice Pending' }
];
document.getElementById('grnTbody').innerHTML = grns.map(g=>`
<tr>
<td><code style="color:var(--primary-light);font-size:11.5px">${g.id}</code></td>
<td><code style="font-size:11px;color:var(--cyan-light)">${g.po}</code></td>
<td>${g.vendor}</td>
<td>${g.items}</td>
<td style="font-weight:700">${g.qty}</td>
<td>${fmtDate(g.date)}</td>
<td>${g.by}</td>
<td>${g.match}</td>
</tr>`).join('');
}
function renderVendors() {
document.getElementById('vendorGrid').innerHTML = AMS.vendors.map(v=>`
<div class="card" style="cursor:default">
<div class="card-body">
<div class="flex items-center gap-3 mb-3">
<div style="width:40px;height:40px;border-radius:var(--radius-md);background:var(--primary-glow);display:flex;align-items:center;justify-content:center;font-size:18px">🏭</div>
<div><div style="font-weight:700;font-size:13.5px">${v.name}</div><div style="font-size:11px;color:var(--text-muted)">${v.cat}</div></div>
</div>
<div class="info-grid" style="gap:8px;margin-bottom:14px">
<div class="info-item"><div class="info-label">Contact</div><div class="info-value" style="font-size:12px">${v.contact}</div></div>
<div class="info-item"><div class="info-label">GST</div><div class="info-value" style="font-size:11px">${v.gst}</div></div>
<div class="info-item"><div class="info-label">Rating</div><div class="info-value">⭐ ${v.rating}/5</div></div>
<div class="info-item"><div class="info-label">Active Contracts</div><div class="info-value">${v.contracts}</div></div>
</div>
<div class="flex gap-2">
<a href="mailto:${v.email}" class="btn btn-ghost btn-sm flex-1">📧 Email</a>
<button class="btn btn-secondary btn-sm flex-1" onclick="showToast('RFQ','RFQ sent to ${v.name}','success')">Send RFQ</button>
</div>
</div>
</div>`).join('');
}
function savePR(status) {
closeModal('prModal');
showToast(`PR ${status}`, status==='Submitted' ? 'PR sent for Dept Head approval' : 'Draft saved', status==='Submitted'?'success':'info');
}
function saveVendor() { closeModal('vendorModal'); showToast('Vendor Added','Vendor added to directory','success'); }
</script>
</body>
</html>