updated alert modal, fixed vendor view history

This commit is contained in:
ssbg04 committed 2026-05-10 01:47:36 +08:00
1 parent 0b91856248
commit cb0dcbe3d1
8 files changed
+282 -156

No files matched your search

+18 -7
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
// ==========================================
+1
View File
@@ -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'; ?>
+1 -1
View File
@@ -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>