Food Ordering System Part 31 | Managing Order Status with POST Actions in ASP.NET Core MVC


 For Part 31 of your Food Ordering System series, we are tackling the core business logic of the admin panel: Order Status Management. This allows the admin to move an order through its lifecycle—from confirmation to delivery or cancellation.

Learn how to implement order status updates in ASP.NET Core MVC. In Part 31, we build secure HTTP POST actions to confirm, cancel, and update order statuses (Pending, Preparing, Delivered) with real-time feedback using TempData.


Detailed Code Explanation

In this tutorial, we add three critical [HttpPost] actions to our AdminController. Using HttpPost is essential here because these actions modify data in the database.

1. UpdateOrderStatus Action

This is a generic method that accepts any valid status string.

  • How it works: It finds the order by ID, updates the Status property to the value sent from the view (e.g., "Preparing"), and saves changes.

  • Feedback: It uses TempData["Success"] to notify the admin exactly what the status was changed to.

2. ConfirmOrder Action

A dedicated shortcut for moving an order from "Pending" to "Confirmed."

  • Purpose: This is often used for a quick "One-Click" confirmation button in the UI. It hardcodes the status to Confirmed to ensure data integrity for that specific step.

3. CancelOrder Action (With Validation)

This action includes a critical business rule.

  • Safety Check: It checks if (order.Status == "Delivered"). You cannot cancel an order that has already reached the customer.

  • Error Handling: If a cancellation is attempted on a delivered order, it triggers TempData["Error"] to warn the admin.


[HttpPost]
public IActionResult UpdateOrderStatus(int orderId, string status)
{
    if (!IsAdmin()) return RedirectToAction("Index", "Home");

    var order = _context.Orders.Find(orderId);
    if (order == null) return NotFound();

    // Valid statuses: Pending, Confirmed, Preparing, OutForDelivery, Delivered, Cancelled
    order.Status = status;
    _context.SaveChanges();

    TempData["Success"] = $"Order #{orderId} status updated to {status}";
    return RedirectToAction("Orders");
}

[HttpPost]
public IActionResult ConfirmOrder(int orderId)
{
    if (!IsAdmin()) return RedirectToAction("Index", "Home");

    var order = _context.Orders.Find(orderId);
    if (order == null) return NotFound();

    order.Status = "Confirmed";
    _context.SaveChanges();

    TempData["Success"] = $"Order #{orderId} has been confirmed!";
    return RedirectToAction("Orders");
}

[HttpPost]
public IActionResult CancelOrder(int orderId)
{
    if (!IsAdmin()) return RedirectToAction("Index", "Home");

    var order = _context.Orders.Find(orderId);
    if (order == null) return NotFound();

    // Only allow cancellation if not already delivered
    if (order.Status == "Delivered")
    {
        TempData["Error"] = "Cannot cancel delivered orders!";
        return RedirectToAction("Orders");
    }

    order.Status = "Cancelled";
    _context.SaveChanges();

    TempData["Success"] = $"Order #{orderId} has been cancelled!";
    return RedirectToAction("Orders");
}

Enhance your Admin Panel with dynamic action buttons in ASP.NET Core MVC. Learn how to use conditional Razor logic to show specific status update buttons (Confirm, Prepare, Deliver, Cancel) based on the current state of an order.


Detailed Code Explanation

This snippet is designed to be placed inside your Orders.cshtml table. It uses Conditional Rendering to ensure only the relevant buttons are visible at the right time.

  1. State-Dependent Logic:

    • Pending: Shows a "Confirm" button. It includes a JavaScript onsubmit confirmation to prevent accidental clicks.

    • Confirmed: Shows a "Fire" icon button to move the order to "Preparing."

    • Preparing: Shows a "Truck" icon button to set the status to "Out For Delivery."

    • OutForDelivery: Shows a "Check Circle" to mark the order as "Delivered."

  2. Hidden Inputs: Each button is wrapped in a small form. We use <input type="hidden" /> to pass the orderId and the target status to the controller without cluttering the UI.

  3. The Cancellation Rule: The final @if block ensures the "Cancel" button is only visible if the order hasn't already been completed (Delivered) or already terminated (Cancelled).

  4. Bootstrap Integration: We use d-inline classes so multiple buttons (like "Prepare" and "Cancel") can sit side-by-side in the same table cell.


@if (order.Status == "Pending")
{
    <form asp-action="ConfirmOrder" method="post" class="d-inline" onsubmit="return confirm('Confirm this order?');">
        <input type="hidden" name="orderId" value="@order.Id" />
        <button type="submit" class="btn btn-sm btn-success" title="Confirm">
            <i class="bi bi-check-lg"></i>
        </button>
    </form>
}

@if (order.Status == "Confirmed")
{
    <form asp-action="UpdateOrderStatus" method="post" class="d-inline">
        <input type="hidden" name="orderId" value="@order.Id" />
        <input type="hidden" name="status" value="Preparing" />
        <button type="submit" class="btn btn-sm btn-primary" title="Start Preparing">
            <i class="bi bi-fire"></i>
        </button>
    </form>
}
else if (order.Status == "Preparing")
{
    <form asp-action="UpdateOrderStatus" method="post" class="d-inline">
        <input type="hidden" name="orderId" value="@order.Id" />
        <input type="hidden" name="status" value="OutForDelivery" />
        <button type="submit" class="btn btn-sm btn-secondary" title="Out for Delivery">
            <i class="bi bi-truck"></i>
        </button>
    </form>
}
else if (order.Status == "OutForDelivery")
{
    <form asp-action="UpdateOrderStatus" method="post" class="d-inline">
        <input type="hidden" name="orderId" value="@order.Id" />
        <input type="hidden" name="status" value="Delivered" />
        <button type="submit" class="btn btn-sm btn-success" title="Mark Delivered">
            <i class="bi bi-check-circle"></i>
        </button>
    </form>
}

@if (order.Status != "Delivered" && order.Status != "Cancelled")
{
    <form asp-action="CancelOrder" method="post" class="d-inline" onsubmit="return confirm('Cancel this order?');">
        <input type="hidden" name="orderId" value="@order.Id" />
        <button type="submit" class="btn btn-sm btn-danger" title="Cancel">
            <i class="bi bi-x-lg"></i>
        </button>
    </form>
}
Add This code in OrderDetails.cshtml view in admin side, 

Detailed Code Explanation

This code block uses @Model (referencing the single Order we are viewing) to decide which buttons to display. Unlike the small icon buttons in the list view, these are large, prominent buttons designed for the detailed dashboard.

  1. Workflow Reversal (Mark Pending): The first block allows an admin to move an order back to "Pending" if it is currently in any intermediate state (like Confirmed or Preparing). It hides this option if the order is already Finished (Delivered) or terminated (Cancelled).

  2. Sequential Progress:

    • Pending → Confirm: Only shows the "Confirm Order" button when the status is exactly "Pending."

    • Confirmed → Preparing: Switches to "Start Preparing" once confirmed.

    • Preparing → OutForDelivery: Switches to "Out for Delivery" once cooking is finished.

    • OutForDelivery → Delivered: The final step to complete the order lifecycle.

  3. The Safety Valve (Cancel Order): The cancellation button is visible at almost every stage except when the food has already been delivered or is already canceled. It includes a JavaScript confirm() prompt to prevent accidental data changes.

  4. Form Methodology: Every button is its own <form> using method="post". This is crucial for security and ensures that data changes only happen via intentional POST requests to our AdminController.


@if (Model.Status != "Pending" && Model.Status != "Cancelled" && Model.Status != "Delivered")
{
    <form asp-action="UpdateOrderStatus" method="post" class="me-2">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <input type="hidden" name="status" value="Pending" />
        <button type="submit" class="btn btn-warning">Mark Pending</button>
    </form>
}

@if (Model.Status == "Pending")
{
    <form asp-action="ConfirmOrder" method="post" class="me-2">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <button type="submit" class="btn btn-success">Confirm Order</button>
    </form>
}

@if (Model.Status == "Confirmed")
{
    <form asp-action="UpdateOrderStatus" method="post" class="me-2">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <input type="hidden" name="status" value="Preparing" />
        <button type="submit" class="btn btn-primary">Start Preparing</button>
    </form>
}

@if (Model.Status == "Preparing")
{
    <form asp-action="UpdateOrderStatus" method="post" class="me-2">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <input type="hidden" name="status" value="OutForDelivery" />
        <button type="submit" class="btn btn-secondary">Out for Delivery</button>
    </form>
}

@if (Model.Status == "OutForDelivery")
{
    <form asp-action="UpdateOrderStatus" method="post" class="me-2">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <input type="hidden" name="status" value="Delivered" />
        <button type="submit" class="btn btn-success">Mark Delivered</button>
    </form>
}

@if (Model.Status != "Delivered" && Model.Status != "Cancelled")
{
    <form asp-action="CancelOrder" method="post" onsubmit="return confirm('Are you sure you want to cancel this order?');">
        <input type="hidden" name="orderId" value="@Model.Id" />
        <button type="submit" class="btn btn-danger">Cancel Order</button>
    </form>
}

Comments