From 300d90e8a36925415e8a0943227f697e1d0718c2 Mon Sep 17 00:00:00 2001 From: Amyflame <70804508+Amyflame@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:02:04 +0300 Subject: [PATCH] Add reusable error modal component for API and system errors --- assets/css/style.css | 40 +++++++++++++++++++++ assets/js/cart.js | 62 +++++++++++++++++++++++++-------- assets/js/notif.js | 56 ++++++++++++++++++++++++++--- assets/js/orders.js | 40 ++++++++++++++++----- index.php | 12 ++++--- src/dashboard/recent_orders.php | 2 +- src/include/sidebar.php | 28 +++++++++++++-- 7 files changed, 204 insertions(+), 36 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 3d4620a..fce0d6f 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -332,4 +332,44 @@ input { .items-cell li { font-size: 12px; } +} + +/*Modal*/ +#error-modal .modal-content { + border-radius: 14px; + border: none; + box-shadow: 0 10px 30px rgba(0,0,0,0.2); + font-family: 'cremona', serif; +} + +#error-modal .modal-header { + background-color: #d9534f; + border-top-left-radius: 14px; + border-top-right-radius: 14px; + padding: 1rem 1.5rem; +} + +#error-modal .modal-title { + font-weight: 600; + display: flex; + align-items: center; + gap: 10px; +} + +#error-modal .modal-body { + padding: 2rem 1.5rem; + font-size: 1.1rem; + color: #374151; +} + +#error-modal .modal-footer { + border-top: 1px solid #eee; + padding: 1rem; +} + +.modal-backdrop { + z-index: 1040 !important; +} +.modal { + z-index: 1050 !important; } \ No newline at end of file diff --git a/assets/js/cart.js b/assets/js/cart.js index de780ab..e0c3ad6 100644 --- a/assets/js/cart.js +++ b/assets/js/cart.js @@ -20,17 +20,25 @@ function updatePrice(id, price) { // Add selected size to cart function addSelectedToCart(id, name, mediumPrice, largePrice) { + try { + let sizeInput = document.querySelector('input[name="size' + id + '"]:checked'); - let size = document.querySelector('input[name="size' + id + '"]:checked').value; + if (!sizeInput) { + ErrorHandler.show("Please select a size before adding to cart.", "Selection Required"); + return; + } - let price = size === "medium" ? mediumPrice : largePrice; + let size = sizeInput.value; + let price = size === "medium" ? mediumPrice : largePrice; + let itemName = name + " (" + size + ")"; - let itemName = name + " (" + size + ")"; + // Make unique ID for size + let uniqueId = id + "_" + size; - // Make unique ID for size - let uniqueId = id + "_" + size; - - addToCart(uniqueId, itemName, price); + addToCart(uniqueId, itemName, price); + } catch (err) { + ErrorHandler.show("An error occurred while adding the item to the cart.", "System Error"); + } } // Update cart display @@ -135,7 +143,7 @@ document.addEventListener("DOMContentLoaded", () => { function checkout() { if (cart.length === 0) { - alert("Cart is empty!"); + ErrorHandler.show("Your cart is empty! Please add items before placing an order.", "Empty Cart"); return; } @@ -162,7 +170,10 @@ function checkout() { }) }) - .then(res => res.text()) + .then(res => { + if (!res.ok) throw new Error("Server error (HTTP " + res.status + ")"); + return res.text(); + }) .then(response => { @@ -179,7 +190,11 @@ function checkout() { .catch(err => { - alert("Error: " + err); + ErrorHandler.show( + "Could not send the order to the server. Please check your connection or try again later.", + "Order Error" + ); + console.error("Checkout Error:", err); }); } @@ -196,9 +211,26 @@ function updateStatus(orderId, status) { body: JSON.stringify({ id: orderId, status: status }) }) + .then(res => { + if (!res.ok) throw new Error("Failed to connect to server."); + return res.text(); + }) + .then(msg => { + alert("Status Updated: " + msg); + }) + .catch(err => { + ErrorHandler.show( + "Failed to update order status. Please try again.", + "Update Error" + ); + console.error("Update Status Error:", err); + }); +} - .then(res => res.text()) - - .then(msg => alert(msg)); - -} \ No newline at end of file +document.addEventListener("DOMContentLoaded", () => { + const discountInput = document.getElementById("discountPercent"); + if (discountInput) { + discountInput.addEventListener("input", updateCart); + } + updateCart(); +}); \ No newline at end of file diff --git a/assets/js/notif.js b/assets/js/notif.js index ff9f1fb..ee1b907 100644 --- a/assets/js/notif.js +++ b/assets/js/notif.js @@ -1,3 +1,31 @@ +/* ERROR MODAL LOGIC */ +const ErrorHandler = { + bootstrapModal: null, + + init() { + const modalElem = document.getElementById('error-modal'); + if (modalElem) { + if (!this.bootstrapModal) { + this.bootstrapModal = new bootstrap.Modal(modalElem); + } + this.titleElem = document.getElementById('modal-title'); + this.messageElem = document.getElementById('modal-message'); + return true; + } + return false; + }, + + show(message, title = "Something Went Wrong") { + if (this.init()) { + this.titleElem.innerText = title; + this.messageElem.innerText = message; + this.bootstrapModal.show(); + } else { + alert(`${title}: ${message}`); + } + console.error(`[System Error] ${title}: ${message}`); + } +}; (() => { let lastCount = 0; @@ -8,7 +36,11 @@ /* NOTIFICATIONS (GLOBAL)*/ function loadNotifications() { fetch(notifUrl + '?t=' + Date.now()) - .then(res => res.json()) + .then(res => { + if (!res.ok) throw new Error("Database unavailable" + res.status); + return res.json(); + }) + .then(data => { const count = parseInt(data.count) || 0; @@ -27,7 +59,11 @@ lastCount = count; }) - .catch(err => console.error('Notif error:', err)); + + .catch(err => { + ErrorHandler.show("Failed to check for new notifications.", "Notification Error"); + console.error('Notif error:', err); + }); } /* VIEW.PHP (ORDERS GRID)*/ @@ -36,11 +72,18 @@ if (!container) return; // only runs on view.php fetch(orderUrl + '?t=' + Date.now()) - .then(res => res.text()) + .then(res => { + if (!res.ok) throw new Error("HTTP error " + res.status); + return res.text(); + }) + .then(html => { container.innerHTML = html; }) - .catch(err => console.error('Orders error:', err)); + .catch(err => { + ErrorHandler.show("Failed to load order data from the server.", "Data Load Error"); + console.error('Orders error:', err); + }); } /* Dashboard @@ -97,4 +140,7 @@ startAutoRefresh(); }); -})(); \ No newline at end of file +})(); + + +// ErrorHandler.show("Frontend trigger test success!", "System Check"); \ No newline at end of file diff --git a/assets/js/orders.js b/assets/js/orders.js index 17d3c12..64d8538 100644 --- a/assets/js/orders.js +++ b/assets/js/orders.js @@ -4,7 +4,10 @@ const BASE_URL = "http://localhost/Nadine-system/src/order/"; // View Items button function viewItems(orderId) { fetch(`${BASE_URL}order_details.php?order_id=${orderId}&ajax=1`) - .then(res => res.json()) + .then(res => { + if (!res.ok) throw new Error("Failed to fetch order details."); + return res.json(); + }) .then(data => { let html = "