view_orders.php
🧩 Syntax:
<?php
// view_orders.php
// Start the session
session_start();
// Include database connection
include 'includes/db.php';
// Include your authentication and permission functions
include 'includes/auth.php'; // IMPORTANT: Make sure this path is correct!
// === RBAC AUTHORIZATION CHECK ===
// Require the 'order_view_all' permission to view any orders.
// If the user doesn't have it, they'll be redirected to index1.php.
requirePermission('order_view_all', 'index1.php');
// If the script continues past requirePermission(), it means the user has the 'order_view_all' permission.
// Get search and filter parameters from GET request
$search = $_GET['search'] ?? '';
$order_status_filter = $_GET['order_status_filter'] ?? 'All';
$show_delivered = isset($_GET['show_delivered']);
// Initialize message variables for feedback to the user
$message = '';
$message_type = '';
// Check for status messages passed via GET parameters (e.g., after an update)
if (isset($_GET['status']) && isset($_GET['msg'])) {
$message_type = $_GET['status'];
$message = htmlspecialchars($_GET['msg']);
}
// Define the allowed order statuses centrally
$allowed_order_statuses = [
'All',
'New Order',
'Ordered',
'In-stock',
'Pending',
'Confirmed',
'Shipped',
'Delivered',
'Cancelled',
'Re-Ordered'
];
// Build the SQL query dynamically
$sql = "SELECT name, item, code, language, quantity, order_status, notes, order_date, id FROM orders";
$conditions = []; // Array to hold WHERE clause conditions
$params = []; // Array to hold parameters for prepared statement
$types = ''; // String to hold parameter types for bind_param
// Add search condition if a search term is provided
if (!empty($search)) {
$conditions[] = "(name LIKE ? OR item LIKE ? OR code LIKE ? OR language LIKE ? OR notes LIKE ?)";
$searchTerm = '%' . $search . '%';
$params = array_merge($params, [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm]);
$types .= 'sssss';
}
// Add order status filter condition if a specific status is selected
if ($order_status_filter && $order_status_filter !== 'All') {
if (in_array($order_status_filter, $allowed_order_statuses)) {
$conditions[] = "order_status = ?";
$params[] = $order_status_filter;
$types .= 's';
} else {
$order_status_filter = 'All'; // Reset to 'All' if invalid status is passed
}
}
// Add condition to hide delivered orders unless explicitly requested AND NOT filtered by status
if (!$show_delivered && $order_status_filter !== 'Delivered') {
$conditions[] = "order_status != 'Delivered'";
}
// Combine conditions into the WHERE clause if any exist
if (!empty($conditions)) {
$sql .= " WHERE " . implode(" AND ", $conditions);
}
// Always order by order_date descending to show most recent first
$sql .= " ORDER BY order_date DESC";
// Prepare the SQL statement
$stmt = $conn->prepare($sql);
// Bind parameters if there are any
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
// Execute the statement and get the result
$stmt->execute();
$result = $stmt->get_result();
// Close the database connection
$conn->close();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LitOrder - View All Orders</title> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"
/>
<style>
/* Universal box-sizing for consistent layout */
*, *::before, *::after {
box-sizing: border-box;
}
:root {
--primary-color: #4caf50;
--primary-hover-color: #45a049;
--secondary-color: #2196f3;
--text-color: #333;
--background-color: #f4f7f6;
--card-background: #ffffff;
--input-border-color: #ccc;
--input-focus-border-color: var(--primary-color);
--button-text-color: white;
--success-color: #28a745; /* Delivered status green */
--error-color: #dc3545; /* Retaining this for general errors, but using specific reds below */
/* --- Updated Status Background Colors for rows for better distinction --- */
--status-new-order-bg: #F0FFF0; /* NEW: Very Light Green/Honeydew for New Orders, distinct from delivered */
--status-ordered-bg: #f0f8ff; /* Alice Blue - No change */
--status-in-stock-bg: #e0f7fa; /* ADDED: Very light cyan/aqua for In-stock */
--status-pending-bg: #fffbe6; /* Light yellow - No change */
--status-confirmed-bg: #e3f2fd; /* Lighter Blue - Subtle shift from original blue */
--status-shipped-bg: #f3e5f5; /* Light Purple - Significantly different */
--status-delivered-bg: #e9f7ed; /* Light green - No change */
--status-cancelled-bg: #ffe6e6; /* Light Red - Standard red for cancelled */
--status-re-ordered-bg: #fff8e1; /* Extra Light Orange - Very distinct from cancelled */
}
body {
font-family: "Roboto", sans-serif;
margin: 0;
padding: 20px; /* Default desktop padding */
background-color: var(--background-color);
color: var(--text-color);
line-height: 1.6;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
/* box-sizing: border-box; Removed from body, now handled by universal rule */
}
.container {
width: 100%;
max-width: 1100px;
background-color: var(--card-background);
padding: 30px; /* Default desktop padding */
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
/* box-sizing: border-box; Removed from container, now handled by universal rule */
}
h1 {
color: var(--primary-color);
text-align: center;
margin-bottom: 25px;
font-size: 2.2em;
font-weight: 700;
}
.header-controls {
display: flex;
justify-content: space-between; /* Keeps "Add New Order" left, and user menu right */
align-items: center;
margin-bottom: 25px;
flex-wrap: wrap;
gap: 15px; /* Adjust gap if necessary for spacing between items */
}
/* NEW: Quick Filter Buttons Section Styles */
.quick-filter-buttons {
display: flex;
justify-content: center; /* Center the buttons */
gap: 15px; /* Space between buttons */
margin-bottom: 25px; /* Space below this section */
flex-wrap: wrap; /* Allow buttons to wrap on smaller screens */
padding: 10px; /* Some padding around the button group */
background-color: var(--card-background); /* Match container background */
border-radius: 8px; /* Match container border-radius */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* Subtle shadow */
}
.quick-filter-buttons .button {
flex-grow: 1; /* Allow buttons to grow to fill space */
max-width: 220px; /* Limit max width for larger screens */
padding: 10px 15px; /* Slightly adjusted padding for these buttons */
font-size: 0.95em; /* Slightly smaller font if desired */
}
.quick-filter-buttons .button.secondary {
background-color: #00bcd4; /* A shade of cyan for In-stock */
}
.quick-filter-buttons .button.secondary:hover {
background-color: #0097a7; /* Darker cyan on hover */
}
/* END NEW: Quick Filter Buttons Section Styles */
.search-form {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: center;
width: 100%;
margin-bottom: 20px;
padding: 15px;
border: 1px solid var(--input-border-color);
border-radius: 8px;
background-color: #f9f9f9;
}
.search-group {
display: flex;
align-items: center;
gap: 8px;
flex-grow: 1;
flex-basis: auto;
min-width: 180px;
}
.search-form input[type="text"],
.search-form select {
padding: 10px;
border: 1px solid var(--input-border-color);
border-radius: 5px;
font-size: 0.95em;
/* box-sizing: border-box; Handled by universal rule */
flex-grow: 1;
}
.search-form input[type="text"] {
min-width: 150px;
}
.search-form select {
min-width: 120px;
}
.search-form button {
padding: 10px 18px;
background-color: var(--secondary-color);
color: var(--button-text-color);
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 0.95em;
transition: background-color 0.3s ease;
}
.search-form button:hover {
background-color: #1976d2;
}
.search-form button i {
margin-right: 5px;
}
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 20px;
background-color: var(--primary-color);
color: var(--button-text-color);
text-decoration: none;
border-radius: 6px;
font-size: 1em;
font-weight: 500;
transition: background-color 0.3s ease, transform 0.2s ease;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
border: none;
cursor: pointer;
text-align: center;
/* box-sizing: border-box; Handled by universal rule */
}
.button:hover {
background-color: var(--primary-hover-color);
transform: translateY(-2px);
}
.button.secondary {
background-color: var(--secondary-color);
}
.button.secondary:hover {
background-color: #1976d2;
}
.button.logout {
background-color: #f44336;
}
.button.logout:hover {
background-color: #d32f2f;
}
.message {
padding: 12px;
margin-bottom: 20px;
border-radius: 5px;
text-align: center;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.message.success {
background-color: #d4edda;
color: var(--success-color);
border: 1px solid var(--success-color);
}
.message.error {
background-color: #f8d7da;
color: var(--error-color);
border: 1px solid var(--error-color);
}
.orders-table-container {
overflow-x: auto;
width: 100%;
}
.orders-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.orders-table th,
.orders-table td {
padding: 12px 15px;
border: 1px solid #ddd;
text-align: left;
vertical-align: middle;
}
.orders-table th {
background-color: var(--primary-color);
color: var(--button-text-color);
font-weight: 600;
white-space: nowrap;
}
/* Styles for clickable rows */
.orders-table tbody tr.clickable-row {
cursor: pointer;
}
.orders-table tbody tr:hover {
background-color: #e6e6e6;
}
/* --- Updated Status Text Colors for better distinction --- */
.status-new-order {
color: #39FF14; /* NEW: Neon Green - Very bright and eye-catching! */
font-weight: bold;
}
.status-ordered {
color: #4682b4; /* SteelBlue - No change */
font-weight: bold;
}
.status-in-stock { /* ADDED: Text color for In-stock status */
color: #00bcd4; /* Cyan/Turquoise */
font-weight: bold;
}
.status-pending {
color: #ffa500; /* Orange - No change */
font-weight: bold;
}
.status-confirmed {
color: #1a73e8; /* Google Blue - No change */
font-weight: bold;
}
.status-shipped {
color: #673ab7; /* Deep Purple - No change */
font-weight: bold;
}
.status-delivered {
color: var(--success-color); /* Uses the general success color - No change */
font-weight: bold;
}
.status-cancelled {
color: #dc3545; /* Uses the general error color - No change */
font-weight: bold;
}
.status-re-ordered {
color: #ff6f00; /* Amber/Orange - No change */
font-weight: bold;
}
/* Row background colors based on status (these link to the :root variables) */
.orders-table tbody tr.row-status-new-order {
background-color: var(--status-new-order-bg);
}
.orders-table tbody tr.row-status-ordered {
background-color: var(--status-ordered-bg);
}
.orders-table tbody tr.row-status-in-stock { /* ADDED: Row background for In-stock status */
background-color: var(--status-in-stock-bg);
}
.orders-table tbody tr.row-status-pending {
background-color: var(--status-pending-bg);
}
.orders-table tbody tr.row-status-confirmed {
background-color: var(--status-confirmed-bg);
}
.orders-table tbody tr.row-status-shipped {
background-color: var(--status-shipped-bg);
}
.orders-table tbody tr.row-status-delivered {
background-color: var(--status-delivered-bg);
}
.orders-table tbody tr.row-status-cancelled {
background-color: var(--status-cancelled-bg);
}
.orders-table tbody tr.row-status-re-ordered {
background-color: var(--status-re-ordered-bg) !important; /* !important to ensure it overrides .clickable-row:hover or other default row styles */
}
/* Ensure alternating row colors still apply if not overridden by status */
.orders-table tbody tr:nth-child(even):not([class*="row-status-"]) {
background-color: #f2f2f2;
}
.action-buttons {
display: flex;
gap: 8px;
justify-content: center;
flex-wrap: wrap;
}
.button.small {
padding: 6px 12px;
font-size: 0.85em;
flex-grow: 0;
min-width: 50px; /* Keep min-width for desktop view */
}
.button.small.delivered-action-table {
background-color: var(--success-color);
padding: 6px 10px;
}
.button.small.delivered-action-table:hover {
background-color: #218838;
}
/* User menu styles (now positioned by flexbox again) */
.user-menu {
position: relative;
display: inline-block;
font-weight: 500;
user-select: none;
/* `order` property removed from here for desktop */
}
.user-menu-toggle {
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
font-weight: 600;
font-size: 1em;
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
border-radius: 6px;
transition: background-color 0.3s ease;
}
.user-menu-toggle:hover,
.user-menu.open .user-menu-toggle {
background-color: #e0f2e9;
}
.user-menu-toggle i {
font-size: 1.2em;
}
.user-dropdown {
display: none;
position: absolute;
right: 0; /* Important: keeps dropdown aligned to the right of the toggle */
top: calc(100% + 6px);
background-color: var(--card-background);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
border-radius: 8px;
min-width: 180px;
z-index: 1000;
flex-direction: column;
padding: 8px 0;
}
.user-menu.open .user-dropdown {
display: flex;
}
.user-dropdown a {
padding: 10px 18px;
color: var(--text-color);
text-decoration: none;
font-size: 0.95em;
display: flex;
align-items: center;
gap: 8px;
transition: background-color 0.2s ease;
}
.user-dropdown a:hover {
background-color: var(--background-color);
color: var(--primary-color);
}
.user-dropdown a i {
width: 18px;
text-align: center;
color: var(--primary-color);
}
/* --- Responsive for tablets & larger phones --- */
@media (max-width: 768px) {
/* Revert body padding to mobile defaults */
body {
padding: 10px 4px; /* Top/bottom 10px, left/right 4px */
}
/* Adjust container padding to be less on mobile, contributing to wider content */
.container {
padding: 12px 6px; /* Top/bottom 12px, left/right 6px */
}
h1 {
font-size: 1.8em;
}
.header-controls {
flex-direction: column; /* Stacks items vertically on mobile */
align-items: stretch; /* Stretches them to fill available width */
}
.header-controls .button,
.header-controls .user-menu { /* Ensure both main header items go full width */
width: 100%;
margin-bottom: 10px; /* Add some space between stacked items */
/* box-sizing: border-box; Handled by universal rule */
}
/* Remove margin-bottom from the last item in header-controls */
.header-controls .user-menu:last-child {
margin-bottom: 0;
}
/* Align user menu content to the right on mobile */
.user-menu-toggle {
justify-content: flex-end; /* Pushes content to the right */
width: 100%; /* Ensure toggle fills its container */
padding-right: 15px; /* Add some right padding */
}
.user-dropdown {
right: 0; /* Keep dropdown aligned to the right on mobile */
}
/* *** IMPORTANT FIX: Apply order property ONLY on mobile *** */
.user-menu {
order: -1; /* Pushes the user menu to the top when flex-direction is column (on mobile) */
}
/* --- Quick Filter Buttons Mobile Adjustments --- */
.quick-filter-buttons {
flex-direction: column; /* Stack buttons vertically on mobile */
gap: 10px; /* Reduce gap */
margin-top: 15px; /* Space between header controls and these buttons */
width: 100%; /* Full width */
padding: 10px 5px; /* Smaller padding on mobile */
box-shadow: none; /* Remove shadow on mobile for cleaner look */
}
.quick-filter-buttons .button {
width: 100%; /* Make buttons full width on mobile */
max-width: none; /* Remove max-width restriction */
}
/* --- End Quick Filter Buttons Mobile Adjustments --- */
/* --- Search Form adjustments for mobile --- */
.search-form {
flex-direction: column;
align-items: center; /* Center the form elements */
width: 90%; /* Make it slightly less than 100% to keep some margin */
max-width: 300px; /* Set a max-width, effectively around 50% on larger phones */
margin-left: auto; /* Center the form horizontally */
margin-right: auto;
}
.search-form .search-group {
flex-direction: column;
align-items: stretch; /* Stretch labels and inputs within the group */
width: 100%; /* Make sure groups fill the form's width */
}
.search-form input[type="text"],
.search-form select,
.search-form button {
width: 100%; /* Make inputs and buttons fill their container */
}
/* --- End Search Form adjustments --- */
/* General tighter padding for all visible cells on mobile */
.orders-table th,
.orders-table td {
padding: 6px 3px; /* Reduced horizontal padding for all cells */
font-size: 0.85em; /* Default font size for visible columns */
}
.orders-table th {
font-size: 0.75em; /* Make header text smaller as well */
}
/* Hide less important columns on mobile */
.orders-table th:nth-child(3), /* Code */
.orders-table td:nth-child(3),
.orders-table th:nth-child(4), /* Language */
.orders-table td:nth-child(4),
.orders-table th:nth-child(5), /* Quantity */
.orders-table td:nth-child(5),
.orders-table th:nth-child(7), /* Notes */
.orders-table td:nth-child(7) {
display: none;
}
/* Modify item column to show code as well for mobile */
.orders-table td:nth-child(2)::after {
content: " (" attr(data-code) ")"; /* Append data-code attribute */
display: block;
font-size: 0.75em; /* Smaller font for appended code */
color: #666;
}
/* --- Specific styling for Order Date and Status on mobile --- */
/* Order Status (6th column) - Max at ~10 letters and allow wrapping */
.orders-table th:nth-child(6), /* Status header */
.orders-table td:nth-child(6) {
font-size: 0.65em; /* Small font for status */
padding: 6px 1px; /* Extremely tight padding */
min-width: unset; /* Allow shrinking if content is very short */
max-width: 85px; /* Adjusted to roughly fit 10 characters at 0.65em font size */
white-space: normal; /* Allows text to wrap naturally */
word-break: break-word; /* Breaks long words if necessary to fit */
box-sizing: border-box;
}
.orders-table th:nth-child(6) {
min-width: 40px; /* Slightly more minimum width for the 'Status' header */
}
/* Order Date (8th column) - Make visible, very small, and tight (single line) */
.orders-table th:nth-child(8), /* Order Date header */
.orders-table td:nth-child(8) {
display: table-cell; /* Override previous display: none */
font-size: 0.65em; /* Very small font */
white-space: nowrap; /* Keep date on single line */
padding: 6px 1px; /* Very tight padding */
min-width: 50px; /* Ensure date can fit */
box-sizing: border-box;
}
.orders-table th:nth-child(8) {
min-width: 50px; /* Minimum width for the 'Order Date' header */
}
/* --- Specific styles for icon-only buttons on mobile --- */
.action-buttons {
gap: 1px; /* Reduce gap between buttons to the absolute minimum */
}
/* Hide text for delivered button */
.button.small.delivered-action-table span {
display: none;
}
/* Style delivered button to be square icon-only */
.button.small.delivered-action-table {
padding: 6px; /* Smaller square padding */
min-width: unset;
width: 30px; /* Smaller fixed width */
height: 30px; /* Smaller fixed height */
display: flex;
justify-content: center;
align-items: center;
}
/* Remove margin-right from icon for delivered button */
.button.small.delivered-action-table i {
margin-right: 0;
font-size: 0.9em; /* Slightly smaller icon */
}
/* Hide text for edit button */
.button.small.secondary span {
display: none;
}
/* Style edit button to be square icon-only */
.button.small.secondary {
padding: 6px; /* Smaller square padding */
min-width: unset;
width: 30px; /* Smaller fixed width */
height: 30px; /* Smaller fixed height */
display: flex;
justify-content: center;
align-items: center;
}
/* Remove margin-right from icon for edit button */
.button.small.secondary i {
margin-right: 0;
font-size: 0.9em; /* Slightly smaller icon */
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
// User menu toggle functionality
const menu = document.querySelector('.user-menu');
const toggle = document.querySelector('.user-menu-toggle');
if (menu && toggle) {
toggle.addEventListener('click', function (e) {
e.stopPropagation(); // Prevent document click from closing it immediately
menu.classList.toggle('open');
});
// Close dropdown if user clicks outside of it
document.addEventListener('click', function (e) {
if (!menu.contains(e.target)) {
menu.classList.remove('open');
}
});
}
// Make table rows clickable to edit order
const rows = document.querySelectorAll('.orders-table tbody tr');
rows.forEach(row => {
// Get the order ID from the data-order-id attribute on the row
const orderId = row.getAttribute('data-order-id');
if (orderId) {
row.classList.add('clickable-row'); // Add class for visual styling (e.g., cursor pointer)
row.addEventListener('click', function(event) {
// Prevent event from bubbling up if an interactive element (like a button/link) inside the row is clicked
if (event.target.closest('.action-buttons') || event.target.tagName === 'A' || event.target.tagName === 'BUTTON') {
return;
}
// Navigate to the edit page for this order
window.location.href = 'edit_order.php?id=' + orderId;
});
}
});
});
</script>
</head>
<body>
<div class="container">
<h1>LitOrder - View All Orders</h1>
<?php if (isset($message)): // Display success or error messages ?>
<div class="message <?= ($message_type === 'success') ? 'success' : 'error' ?>">
<?= htmlspecialchars($message) ?>
</div>
<?php endif; ?>
<div class="header-controls">
<a href="add_order.php" class="button">
<i class="fas fa-plus-circle"></i> Add New Order
</a>
<div class="user-menu">
<button class="user-menu-toggle" aria-haspopup="true" aria-expanded="false" aria-label="User menu toggle">
<i class="fas fa-user-circle"></i>
Welcome, <?= htmlspecialchars($_SESSION['username']); ?>
<i class="fas fa-caret-down"></i>
</button>
<div class="user-dropdown" role="menu" aria-label="User menu">
<a href="manage_user.php" role="menuitem">
<i class="fas fa-cog"></i> Manage My Details
</a>
<a href="logout.php" role="menuitem">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
</div>
</div>
</div>
<div class="quick-filter-buttons">
<a href="view_orders.php?order_status_filter=In-stock" class="button secondary">
<i class="fas fa-boxes"></i> View In-stock Orders
</a>
<a href="view_orders.php?order_status_filter=New+Order" class="button primary">
<i class="fas fa-star"></i> View New Orders
</a>
</div>
<form method="GET" action="" class="search-form" role="search" aria-label="Order search and filter form">
<div class="search-group">
<label for="search">Search:</label>
<input
type="text"
id="search"
name="search"
value="<?= htmlspecialchars($search) ?>"
placeholder="Name, Item, Code, Language, Notes"
aria-describedby="search-desc"
/>
</div>
<div class="search-group">
<label for="order_status_filter">Order Status:</label>
<select id="order_status_filter" name="order_status_filter" aria-describedby="status-desc">
<?php foreach ($allowed_order_statuses as $status): ?>
<option value="<?= htmlspecialchars($status) ?>" <?= (isset($order_status_filter) && $order_status_filter === $status) ? 'selected' : '' ?>>
<?= htmlspecialchars($status) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="search-group" style="align-items:center;">
<input
type="checkbox"
id="show_delivered"
name="show_delivered"
<?= (isset($show_delivered) && $show_delivered) ? 'checked' : '' ?>
/>
<label for="show_delivered">Show Delivered</label>
</div>
<button type="submit" aria-label="Apply filters">
<i class="fas fa-search"></i> Search
</button>
</form>
<div class="orders-table-container" role="region" aria-live="polite" aria-label="Orders list">
<table class="orders-table" role="table" aria-describedby="table-desc">
<caption id="table-desc" class="sr-only">List of orders with details and actions</caption>
<thead>
<tr>
<th>Name</th>
<th>Item (Code)</th>
<th>Code</th>
<th>Language</th>
<th>Quantity</th>
<th>Status</th>
<th>Notes</th>
<th>Order Date</th>
<?php if (hasPermission('order_edit')): // Only show actions column if user has edit permission (no delete here) ?>
<th>Actions</th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php if ($result->num_rows > 0): ?>
<?php while($row = $result->fetch_assoc()): ?>
<?php
// Determine the status class for the row
$rowStatusClass = '';
if (isset($row['order_status'])) {
$statusSlug = strtolower(str_replace([' ', '-'], '', $row['order_status']));
$rowStatusClass = 'row-status-' . $statusSlug;
}
// Prepare order ID for buttons
$orderId = htmlspecialchars($row['id']);
?>
<tr data-order-id="<?= $orderId ?>" class="<?= htmlspecialchars($rowStatusClass) ?>">
<td><?= htmlspecialchars($row['name']) ?></td>
<td data-code="<?= htmlspecialchars($row['code']) ?>"><?= htmlspecialchars($row['item']) ?></td>
<td><?= htmlspecialchars($row['code']) ?></td>
<td><?= htmlspecialchars($row['language']) ?></td>
<td><?= htmlspecialchars($row['quantity']) ?></td>
<td><span class="status-<?= strtolower(str_replace(' ', '', $row['order_status'])) ?>"><?= htmlspecialchars($row['order_status']) ?></span></td>
<td><?= htmlspecialchars($row['notes']) ?></td>
<td><?= htmlspecialchars(date('Y-m-d', strtotime($row['order_date']))) ?></td>
<?php if (hasPermission('order_edit')): // Only show actions column if user has edit permission ?>
<td class="action-buttons">
<?php if (hasPermission('order_edit')): ?>
<a href="edit_order.php?id=<?= $orderId ?>" class="button small secondary" title="Edit Order">
<i class="fas fa-edit"></i> <span>Edit</span>
</a>
<?php endif; ?>
<?php if ($row['order_status'] !== 'Delivered'): // Show "Delivered" button only if not delivered ?>
<?php if (hasPermission('order_edit')): // Assuming 'order_edit' permission is needed to mark as delivered ?>
<form action="mark_delivered.php" method="POST" style="display:inline-block; margin-left: 5px;">
<input type="hidden" name="id" value="<?= $orderId ?>">
<input type="hidden" name="redirect_url" value="view_orders.php">
<button type="submit" class="button small success-action-table" title="Mark as Delivered">
<i class="fas fa-check-circle"></i> <span>Delivered</span>
</button>
</form>
<?php endif; ?>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endwhile; ?>
<?php else: ?>
<tr><td colspan="9">No orders found.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</body>
</html>