updated alert modal, fixed vendor view history
This commit is contained in:
1 parent
0b91856248
commit
cb0dcbe3d1
8 files changed
+282
-156
No files matched your search
+18
-7
@@ -1,7 +1,18 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
# Install required extensions
|
||||
RUN docker-php-ext-install mysqli pdo pdo_mysql
|
||||
# Set Philippines timezone
|
||||
ENV TZ=Asia/Manila
|
||||
|
||||
# Install required packages + PHP extensions
|
||||
RUN apt-get update && apt-get install -y tzdata && \
|
||||
docker-php-ext-install mysqli pdo pdo_mysql
|
||||
|
||||
# Configure system timezone
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
|
||||
echo $TZ > /etc/timezone
|
||||
|
||||
# Set PHP timezone
|
||||
RUN echo "date.timezone = Asia/Manila" > /usr/local/etc/php/conf.d/timezone.ini
|
||||
|
||||
# Enable Apache modules
|
||||
RUN a2enmod rewrite headers
|
||||
@@ -9,19 +20,19 @@ RUN a2enmod rewrite headers
|
||||
# Copy project
|
||||
COPY . /var/www/html/
|
||||
|
||||
# Fix permissions (IMPORTANT FIXED TYPO)
|
||||
# Fix permissions
|
||||
RUN chown -R www-data:www-data /var/www/html/
|
||||
|
||||
# Ensure Apache allows .htaccess (API routing support)
|
||||
# Ensure Apache allows .htaccess
|
||||
RUN sed -i 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf
|
||||
|
||||
# Optional: clean API-friendly Apache config
|
||||
# Optional Apache config
|
||||
RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
|
||||
|
||||
# Copy the default production PHP configuration
|
||||
# Copy production PHP config
|
||||
RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||
|
||||
# Force PHP to allow Environment Variables in the $_ENV array
|
||||
# Allow environment variables in PHP
|
||||
RUN sed -i 's/variables_order = "GPCS"/variables_order = "EGPCS"/g' "$PHP_INI_DIR/php.ini"
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "https://openrouter.ai/api",
|
||||
"ANTHROPIC_AUTH_TOKEN": "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",
|
||||
"ANTHROPIC_API_KEY": "",
|
||||
"ANTHROPIC_MODEL": "openrouter/free"
|
||||
}
|
||||
}
|
||||
+157
-131
@@ -1,131 +1,157 @@
|
||||
:root {
|
||||
--bg-color: #f3f4f6;
|
||||
--card-bg: #ffffff;
|
||||
--primary-accent: #4f46e5;
|
||||
--primary-hover: #4338ca;
|
||||
--text-main: #1f2937;
|
||||
--text-muted: #6b7280;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-main);
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* Navbar Modernization */
|
||||
.navbar-custom {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1020;
|
||||
}
|
||||
|
||||
/* Sleek Cards */
|
||||
.card-custom {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.kpi-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* Modern Tabs */
|
||||
.nav-pills {
|
||||
background: #e5e7eb;
|
||||
padding: 0.35rem;
|
||||
border-radius: 12px;
|
||||
display: inline-flex;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.nav-pills .nav-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link:hover {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--primary-accent);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Responsive Tables */
|
||||
.table-custom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.table-custom th {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 2px solid #e5e7eb;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.table-custom td {
|
||||
vertical-align: middle;
|
||||
padding: 1rem 0.75rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
/* Form styling */
|
||||
.form-control, .form-select {
|
||||
border-radius: 10px;
|
||||
border: 1px solid #d1d5db;
|
||||
padding: 0.6rem 1rem;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary-accent);
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary {
|
||||
background-color: var(--primary-accent);
|
||||
border-color: var(--primary-accent);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Mobile Specific Hiding Rules */
|
||||
@media (max-width: 768px) {
|
||||
.hide-on-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
.header-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch !important;
|
||||
gap: 1rem;
|
||||
}
|
||||
.header-actions button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
:root {
|
||||
--bg-color: #f3f4f6;
|
||||
--card-bg: #ffffff;
|
||||
--primary-accent: #4f46e5;
|
||||
--primary-hover: #4338ca;
|
||||
--text-main: #1f2937;
|
||||
--text-muted: #6b7280;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-main);
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* Navbar Modernization */
|
||||
.navbar-custom {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1020;
|
||||
}
|
||||
|
||||
/* Sleek Cards */
|
||||
.card-custom {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.kpi-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* Modern Tabs */
|
||||
.nav-pills {
|
||||
background: #e5e7eb;
|
||||
padding: 0.35rem;
|
||||
border-radius: 12px;
|
||||
display: inline-flex;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
flex-wrap: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.nav-pills .nav-item {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
color: var(--text-muted);
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link:hover {
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active {
|
||||
background-color: var(--card-bg);
|
||||
color: var(--primary-accent);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* Responsive Tables */
|
||||
.table-custom {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.table-custom th {
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 2px solid #e5e7eb;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.table-custom td {
|
||||
vertical-align: middle;
|
||||
padding: 1rem 0.75rem;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
/* Form styling */
|
||||
.form-control, .form-select {
|
||||
border-radius: 10px;
|
||||
border: 1px solid #d1d5db;
|
||||
padding: 0.6rem 1rem;
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--primary-accent);
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn-primary {
|
||||
background-color: var(--primary-accent);
|
||||
border-color: var(--primary-accent);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Mobile Specific Hiding Rules */
|
||||
@media (max-width: 768px) {
|
||||
.hide-on-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
.header-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch !important;
|
||||
gap: 1rem;
|
||||
}
|
||||
.header-actions button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Toasts */
|
||||
.toast-custom {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.toast-header-custom {
|
||||
background: transparent;
|
||||
border-bottom: none;
|
||||
padding: 1rem 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.toast-body-custom {
|
||||
padding: 0.5rem 1rem 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.toast-custom.toast-success { border-left: 5px solid #10b981; }
|
||||
.toast-custom.toast-danger { border-left: 5px solid #ef4444; }
|
||||
.toast-custom.toast-warning { border-left: 5px solid #f59e0b; }
|
||||
.toast-custom.toast-info { border-left: 5px solid #3b82f6; }␍
|
||||
+16
-1
@@ -434,7 +434,22 @@ $(document).ready(function () {
|
||||
</tr>`;
|
||||
});
|
||||
$("#lowStockTableBody").html(rows);
|
||||
setTimeout(() => { $("#lowStockModal").modal("show"); }, 500);
|
||||
// Replace modal with notification
|
||||
setTimeout(() => {
|
||||
if ('Notification' in window) {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === 'granted') {
|
||||
new Notification('Low Stock Alert!', {
|
||||
body: 'Items below reorder level detected',
|
||||
tag: 'lowStockAlert'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback to alert if notifications not supported
|
||||
alert('Low Stock Alert! Items below reorder level detected');
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-2
@@ -245,7 +245,6 @@ $(document).ready(function () {
|
||||
});
|
||||
});
|
||||
|
||||
// --- 5. Low Stock Alerts ---
|
||||
function checkAndShowLowStockAlert(inventoryData) {
|
||||
let lowStockItems = inventoryData.filter(item => parseInt(item.quantity) <= parseInt(item.reorder_level));
|
||||
|
||||
@@ -260,10 +259,23 @@ $(document).ready(function () {
|
||||
</tr>`;
|
||||
});
|
||||
$("#lowStockTableBody").html(rows);
|
||||
setTimeout(() => { $("#lowStockModal").modal("show"); }, 500);
|
||||
|
||||
// Show premium notification instead of modal
|
||||
showNotification({
|
||||
title: 'Low Stock Alert!',
|
||||
message: `${lowStockItems.length} items are below reorder level.`,
|
||||
type: 'danger',
|
||||
icon: 'bi-exclamation-triangle-fill',
|
||||
action: {
|
||||
label: 'View & Restock',
|
||||
callback: 'viewLowStock()'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$(document).on("click", "#manualRestockBtn", function() {
|
||||
$("#lowStockModal").modal("hide");
|
||||
switchTab('po');
|
||||
|
||||
+67
-14
@@ -24,25 +24,78 @@ let spendChartInstance = null;
|
||||
// ==========================================
|
||||
// 2. GLOBAL HELPER FUNCTIONS
|
||||
// ==========================================
|
||||
function showAlert(msg, type) {
|
||||
// Create a unique ID for this alert so the timer doesn't accidentally hide a newer alert
|
||||
let alertId = 'alert-' + Date.now();
|
||||
function showAlert(msg, type = 'info') {
|
||||
const toastId = 'toast-' + Date.now();
|
||||
const iconMap = {
|
||||
'success': 'bi-check-circle-fill',
|
||||
'danger': 'bi-exclamation-octagon-fill',
|
||||
'warning': 'bi-exclamation-triangle-fill',
|
||||
'info': 'bi-info-circle-fill'
|
||||
};
|
||||
const icon = iconMap[type] || 'bi-bell-fill';
|
||||
const title = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
|
||||
$("#alertBox").html(`
|
||||
<div id="${alertId}" class="alert alert-${type} alert-dismissible fade show border-0 shadow-sm" style="z-index: 1050;">
|
||||
${msg}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
const toastHtml = `
|
||||
<div id="${toastId}" class="toast toast-custom toast-${type} fade hide" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false">
|
||||
<div class="toast-header toast-header-custom">
|
||||
<i class="bi ${icon} text-${type} me-2"></i>
|
||||
<strong class="me-auto text-${type}">${title}</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body toast-body-custom">
|
||||
${msg}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
`;
|
||||
|
||||
// Auto-hide the alert after 4 seconds (4000 milliseconds)
|
||||
setTimeout(function() {
|
||||
$("#" + alertId).fadeOut(400, function() {
|
||||
$(this).remove(); // Completely removes it from the screen
|
||||
});
|
||||
}, 4000);
|
||||
$("#toastContainer").append(toastHtml);
|
||||
|
||||
const toastElement = document.getElementById(toastId);
|
||||
const toast = new bootstrap.Toast(toastElement, { autohide: false });
|
||||
toast.show();
|
||||
|
||||
toastElement.addEventListener('hidden.bs.toast', function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Global function for complex notifications (with buttons/links)
|
||||
function showNotification(options) {
|
||||
const toastId = 'notif-' + Date.now();
|
||||
const type = options.type || 'info';
|
||||
const icon = options.icon || 'bi-bell-fill';
|
||||
|
||||
const toastHtml = `
|
||||
<div id="${toastId}" class="toast toast-custom toast-${type} fade hide" role="alert" aria-live="assertive" aria-atomic="true" data-bs-autohide="false" style="min-width: 300px;">
|
||||
<div class="toast-header toast-header-custom">
|
||||
<i class="bi ${icon} text-${type} me-2"></i>
|
||||
<strong class="me-auto text-${type}">${options.title || 'Notification'}</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body toast-body-custom">
|
||||
<div class="mb-2">${options.message}</div>
|
||||
${options.action ? `<button class="btn btn-sm btn-${type} rounded-pill px-3 mt-2" onclick="${options.action.callback}">${options.action.label}</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$("#toastContainer").append(toastHtml);
|
||||
|
||||
const toastElement = document.getElementById(toastId);
|
||||
const toast = new bootstrap.Toast(toastElement, { autohide: false });
|
||||
toast.show();
|
||||
|
||||
toastElement.addEventListener('hidden.bs.toast', function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
// Dedicated function for opening low stock modal
|
||||
window.viewLowStock = function() {
|
||||
$("#lowStockModal").modal("show");
|
||||
};
|
||||
|
||||
|
||||
// ==========================================
|
||||
// 3. SPA LAZY LOADING ROUTER
|
||||
// ==========================================
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
<div class="container" id="dashboardSection">
|
||||
|
||||
<div id="toastContainer" class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 2000;"></div>
|
||||
<div id="alertBox"></div>
|
||||
|
||||
<?php include 'partials/navbar.php'; ?>
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
</div>
|
||||
<div class="modal-body px-4 py-4">
|
||||
|
||||
<div class="table-responsive border rounded-3 overflow-hidden">
|
||||
<div class="table-responsive border rounded-3 overflow-auto">
|
||||
<table class="table table-hover table-custom mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
|
||||
Reference in new issue
Block a user