488 lines
18 KiB
HTML
488 lines
18 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>ICIS Inventory System</title>
|
|
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
<link
|
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
|
rel="stylesheet"
|
|
/>
|
|
<link
|
|
rel="stylesheet"
|
|
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css"
|
|
/>
|
|
|
|
<style>
|
|
body {
|
|
font-family: "Inter", sans-serif;
|
|
background-color: #f8f9fa;
|
|
}
|
|
.card-custom {
|
|
border: none;
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.04);
|
|
}
|
|
.table-custom th {
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
font-size: 0.8rem;
|
|
letter-spacing: 0.5px;
|
|
color: #6c757d;
|
|
}
|
|
.table-custom td {
|
|
vertical-align: middle;
|
|
}
|
|
/* Navbar */
|
|
.navbar-custom {
|
|
background-color: #ffffff;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
|
|
}
|
|
</style>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="dashboardSection">
|
|
<nav class="navbar navbar-expand-lg navbar-custom py-3 mb-4">
|
|
<div class="container">
|
|
<a
|
|
class="navbar-brand fw-bold d-flex align-items-center gap-2"
|
|
href="#"
|
|
>
|
|
<i class="bi bi-box-seam text-primary"></i> Inventory Pro
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container">
|
|
<div id="alertBox"></div>
|
|
|
|
<ul class="nav nav-pills mb-4" id="dashboardTabs">
|
|
<li class="nav-item">
|
|
<a
|
|
class="nav-link active"
|
|
href="#"
|
|
id="tab-inventory"
|
|
onclick="switchTab('inventory')"
|
|
>Inventory View</a
|
|
>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#" id="tab-po" onclick="switchTab('po')"
|
|
>Purchase Orders</a
|
|
>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a
|
|
class="nav-link"
|
|
href="#"
|
|
id="tab-analytics"
|
|
onclick="switchTab('analytics')"
|
|
><i class="bi bi-graph-up"></i> Reports & Analytics</a
|
|
>
|
|
</li>
|
|
</ul>
|
|
|
|
<div id="inventoryContainer">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h4 class="fw-bold mb-0">Overview</h4>
|
|
<button
|
|
class="btn btn-primary shadow-sm rounded-pill px-4"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#addItemModal"
|
|
>
|
|
<i class="bi bi-plus-lg"></i> Add Item
|
|
</button>
|
|
</div>
|
|
|
|
<div class="card card-custom">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover table-custom mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">Item Details</th>
|
|
<th>Supplier</th>
|
|
<th>Unit Price</th>
|
|
<th>Stock Level</th>
|
|
<th class="pe-4 text-end">Status</th>
|
|
<th class="pe-4 text-end">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="inventoryTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card card-custom" id="poContainer" style="display: none">
|
|
<div
|
|
class="card-header bg-white d-flex justify-content-between align-items-center py-3"
|
|
>
|
|
<h5 class="mb-0 fw-bold">Recent Purchase Orders</h5>
|
|
<button
|
|
class="btn btn-primary shadow-sm rounded-pill px-4"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#createPOModal"
|
|
>
|
|
<i class="bi bi-cart-plus"></i> Create PO
|
|
</button>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover table-custom mb-0">
|
|
<thead class="bg-light">
|
|
<tr>
|
|
<th class="ps-4">PO #</th>
|
|
<th>Date</th>
|
|
<th>Supplier</th>
|
|
<th>Total Amount</th>
|
|
<th>Status</th>
|
|
<th class="pe-4 text-end">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="poTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="analyticsContainer" style="display: none">
|
|
<div class="card card-custom">
|
|
<div
|
|
class="card-header bg-white border-bottom d-flex justify-content-between align-items-center py-3 px-4"
|
|
>
|
|
<h5 class="mb-0 fw-bold">Dashboard Overview</h5>
|
|
<button
|
|
class="btn btn-sm btn-outline-primary shadow-sm rounded-pill px-3"
|
|
onclick="loadAnalytics(this)"
|
|
>
|
|
<i class="bi bi-arrow-clockwise"></i> Refresh Data
|
|
</button>
|
|
</div>
|
|
|
|
<div class="card-body p-4">
|
|
<div class="row g-4 mb-4">
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-light border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-muted small fw-bold mb-2">
|
|
STOCK VALUATION
|
|
</div>
|
|
<h3 id="statTotalValue" class="fw-bold text-primary mb-0">
|
|
₱0.00
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-light border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-muted small fw-bold mb-2">
|
|
TOTAL PO SPEND
|
|
</div>
|
|
<h3 id="statTotalSpend" class="fw-bold text-success mb-0">
|
|
₱0.00
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-light border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-muted small fw-bold mb-2">
|
|
INVENTORY TURNOVER
|
|
</div>
|
|
<h3 id="statTurnover" class="fw-bold text-dark mb-0">
|
|
0.00
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4 mb-5">
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-light border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-muted small fw-bold mb-2">TOTAL ITEMS</div>
|
|
<h3 id="statTotalItems" class="fw-bold text-dark mb-0">
|
|
0
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-danger bg-opacity-10 border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-danger small fw-bold mb-2">
|
|
SHORTAGES (LOW STOCK)
|
|
</div>
|
|
<h3 id="statLowStock" class="fw-bold text-danger mb-0">
|
|
0
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-4">
|
|
<div
|
|
class="card bg-warning bg-opacity-10 border-0 shadow-none p-4 text-center h-100 rounded-3"
|
|
>
|
|
<div class="text-warning small fw-bold mb-2">
|
|
OVERSTOCK ALERTS
|
|
</div>
|
|
<h3 id="statOverstock" class="fw-bold text-warning mb-0">
|
|
0
|
|
</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-12 col-lg-6">
|
|
<h6
|
|
class="fw-bold mb-4 text-secondary text-uppercase"
|
|
style="letter-spacing: 0.5px"
|
|
>
|
|
Stock Valuation by Supplier
|
|
</h6>
|
|
<div style="position: relative; height: 35vh; width: 100%">
|
|
<canvas id="valuationChart"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-lg-6">
|
|
<h6
|
|
class="fw-bold mb-4 text-secondary text-uppercase"
|
|
style="letter-spacing: 0.5px"
|
|
>
|
|
Spend by Category (Supplier)
|
|
</h6>
|
|
<div style="position: relative; height: 35vh; width: 100%">
|
|
<canvas id="spendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="addItemModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow">
|
|
<form id="addItemForm">
|
|
<div class="modal-header border-bottom-0 pb-0">
|
|
<h5 class="modal-title fw-bold">Add New Item</h5>
|
|
<button
|
|
type="button"
|
|
class="btn-close"
|
|
data-bs-dismiss="modal"
|
|
></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted small">Item Name</label>
|
|
<input
|
|
type="text"
|
|
name="name"
|
|
class="form-control bg-light border-0"
|
|
required
|
|
/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted small"
|
|
>Description (Include Location / Serial No. for
|
|
Equipment)</label
|
|
>
|
|
<textarea
|
|
name="description"
|
|
class="form-control bg-light border-0"
|
|
rows="2"
|
|
placeholder="e.g., Stored in Science Lab 302. Serial: #XYZ123"
|
|
></textarea>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label text-muted small">Initial Qty</label>
|
|
<input
|
|
type="number"
|
|
name="quantity"
|
|
class="form-control bg-light border-0"
|
|
value="0"
|
|
/>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label text-muted small">Price (₱)</label>
|
|
<input
|
|
type="number"
|
|
name="unit_price"
|
|
step="0.01"
|
|
class="form-control bg-light border-0"
|
|
required
|
|
/>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label text-muted small">Reorder At</label>
|
|
<input
|
|
type="number"
|
|
name="reorder_level"
|
|
class="form-control bg-light border-0"
|
|
value="10"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted small">Supplier</label>
|
|
<select
|
|
name="supplier_id"
|
|
id="supplierSelect"
|
|
class="form-select bg-light border-0"
|
|
required
|
|
>
|
|
<option value="">Select...</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-top-0 pt-0">
|
|
<button
|
|
type="button"
|
|
class="btn btn-light"
|
|
data-bs-dismiss="modal"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button type="submit" class="btn btn-primary px-4">
|
|
Save Item
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
$(document).ready(function () {
|
|
// Initialize App directly
|
|
loadInventory();
|
|
loadSuppliers();
|
|
|
|
// Data Loading functions
|
|
function loadInventory() {
|
|
$.ajax({
|
|
url: "api.php?action=get_inventory",
|
|
type: "GET",
|
|
cache: false,
|
|
dataType: "json",
|
|
success: function (res) {
|
|
if (res.status === "success") {
|
|
let rows = "";
|
|
res.data.forEach(function (item) {
|
|
let isLowStock =
|
|
parseInt(item.quantity) <= parseInt(item.reorder_level);
|
|
let statusBadge = isLowStock
|
|
? '<span class="badge bg-danger bg-opacity-10 text-danger border border-danger border-opacity-25 rounded-pill px-3 py-2"><i class="bi bi-exclamation-triangle-fill me-1"></i> Low Stock</span>'
|
|
: '<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25 rounded-pill px-3 py-2"><i class="bi bi-check-circle-fill me-1"></i> Healthy</span>';
|
|
|
|
rows += `<tr>
|
|
<td class="ps-4">
|
|
<div class="fw-bold text-dark">${item.name}</div>
|
|
<div class="text-muted small">${item.description || "No description"}</div>
|
|
<div class="text-muted small">ID: #${item.inventory_id}</div>
|
|
</td>
|
|
<td><span class="badge bg-light text-dark border">${item.supplier_name || "N/A"}</span></td>
|
|
<td class="fw-medium">₱${parseFloat(item.unit_price).toFixed(2)}</td>
|
|
<td>
|
|
<div class="fw-bold ${isLowStock ? "text-danger" : ""}">${item.quantity} units</div>
|
|
<div class="text-muted small">Reorder at ${item.reorder_level}</div>
|
|
</td>
|
|
<td class="text-end">${statusBadge}</td>
|
|
<td class="pe-4 text-end">
|
|
<button class="btn btn-sm btn-light text-warning me-1 issue-btn" data-id="${item.inventory_id}" data-name="${item.name}"><i class="bi bi-box-arrow-up"></i></button>
|
|
<button class="btn btn-sm btn-light text-primary me-1 edit-btn" data-id="${item.inventory_id}" data-name="${item.name}" data-desc="${item.description}" data-qty="${item.quantity}" data-price="${item.unit_price}" data-reorder="${item.reorder_level}" data-supplier="${item.supplier_id}"><i class="bi bi-pencil"></i></button>
|
|
<button class="btn btn-sm btn-light text-danger delete-btn" data-id="${item.inventory_id}"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
</tr>`;
|
|
});
|
|
$("#inventoryTableBody").html(rows);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
function loadSuppliers() {
|
|
$.ajax({
|
|
url: "api.php?action=get_suppliers",
|
|
type: "GET",
|
|
dataType: "json",
|
|
success: function (res) {
|
|
if (res.status === "success") {
|
|
let options = '<option value="">Select...</option>';
|
|
res.data.forEach(function (sup) {
|
|
options += `<option value="${sup.supplier_id}">${sup.name}</option>`;
|
|
});
|
|
$("#supplierSelect").html(options);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
// Add Item Handling
|
|
$("#addItemForm").submit(function (e) {
|
|
e.preventDefault();
|
|
$.ajax({
|
|
url: "api.php?action=add_item",
|
|
type: "POST",
|
|
data: $(this).serialize(),
|
|
dataType: "json",
|
|
success: function (res) {
|
|
if (res.status === "success") {
|
|
$("#addItemModal").modal("hide");
|
|
$("#addItemForm")[0].reset();
|
|
showAlert(res.message, "success");
|
|
loadInventory();
|
|
} else {
|
|
showAlert(res.message, "danger");
|
|
}
|
|
},
|
|
});
|
|
});
|
|
|
|
// Tab Switching Logic
|
|
window.switchTab = function (tab) {
|
|
$(".nav-link").removeClass("active");
|
|
$("#tab-" + tab).addClass("active");
|
|
$("#inventoryContainer, #poContainer, #analyticsContainer").hide();
|
|
|
|
if (tab === "inventory") {
|
|
$("#inventoryContainer").fadeIn();
|
|
loadInventory();
|
|
} else if (tab === "po") {
|
|
$("#poContainer").fadeIn();
|
|
// loadPOs logic here
|
|
} else if (tab === "analytics") {
|
|
$("#analyticsContainer").fadeIn();
|
|
loadAnalytics();
|
|
}
|
|
};
|
|
|
|
// Helper function for alerts
|
|
function showAlert(msg, type) {
|
|
$("#alertBox").html(
|
|
`<div class="alert alert-${type} alert-dismissible fade show border-0 shadow-sm">${msg}<button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>`,
|
|
);
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|