Food Ordering System Part 18 | How to Create an Order History Page with Filters in ASP.NET Core MVC


In Part 18 of our Food Ordering System, we are shifting from the checkout process to the user dashboard. This MyOrders action is the engine behind the "Order History" page, ensuring that customers can only see their own orders while retrieving all the necessary food details from the database.


Step-by-Step Code Explanation

  1. Method Parameters: The action accepts status, sortOrder, fromDate, and toDate. While this specific snippet focuses on the core retrieval, these parameters allow for the advanced filtering we will implement in the view.

  2. Session Security:

    • HttpContext.Session.GetInt32("UserId") retrieves the unique ID of the logged-in user.

    • The if (userId == null) check acts as a security gate—if the session has expired or the user isn't logged in, they are sent straight to the Login page.

  3. Eager Loading (Include & ThenInclude):

    • .Include(o => o.OrderItems) tells Entity Framework to join the Orders table with the OrderItems table.

    • .ThenInclude(oi => oi.FoodItem) goes a step deeper to grab the name and details of the specific food (e.g., Pizza, Burger) associated with those items. This prevents "Lazy Loading" errors in your HTML view.

  4. Ownership Filter: .Where(o => o.UserId == userId) is the most critical line for privacy. It ensures a customer cannot see someone else's order history by changing a URL ID.

  5. AsQueryable() and Execution:

    • AsQueryable() prepares the query for further filtering (like by date or status).

    • orders.ToList() finally executes the command, converts the database results into a C# List, and sends it to the View.

ORDERCONTROLLER.CS (PART 18)
public IActionResult MyOrders()
{
    var userId = HttpContext.Session.GetInt32("UserId");
    if (userId == null) return RedirectToAction("Login", "Account");

    var orders = _context.Orders
        .Include(o => o.OrderItems)
        .ThenInclude(oi => oi.FoodItem)
        .Where(o => o.UserId == userId)
        .AsQueryable();

    return View(orders.ToList());
}

We are bringing the user's journey full circle by designing the My Orders View. This page is a sophisticated dashboard that allows customers to view their purchase history, check real-time order statuses, and see a breakdown of their favorite meals.


Step-by-Step Code Explanation

  1. Empty State Handling: The code first checks @if (!Model.Any()). This is a crucial UX step—if a user has never ordered, we show a friendly alert with a direct link back to the menu instead of a blank screen.

  2. Responsive Grid Layout: We use the Bootstrap row and col-md-6 classes. This creates a modern two-column layout on desktops that stacks perfectly into a single column on mobile devices.

  3. Dynamic Status Badges: Inside the @foreach loop, we use a C# switch expression to assign specific Bootstrap colors to order statuses:

    • Success (Green) for "Delivered."

    • Warning (Yellow) for "Pending."

    • Danger (Red) for "Cancelled."

  4. Date & Currency Formatting: We use .ToString("MMM dd, yyyy") for a professional date look and "F2" for currency to ensure prices always show two decimal places (e.g., $10.00).

  5. Smart Item Preview: To keep the dashboard clean, we use .Take(3) to show only the first three items in an order. If there are more, it dynamically calculates and displays a "+ X more items" label.


VIEWS/ORDER/MYORDERS.CSHTML
@model List<Order>
@{
    ViewData["Title"] = "My Orders";
}

<div class="container my-5">
    <h2 class="mb-4"><i class="bi bi-bag-check"></i> My Orders</h2>

    @if (!Model.Any())
    {
        <div class="alert alert-info">
            <i class="bi bi-info-circle"></i> No orders found matching your criteria.
            <a href="/Menu" class="alert-link">Browse our menu</a> to place an order!
        </div>
    }
    else
    {
        <div class="row">
            @foreach (var order in Model)
            {
                <div class="col-md-6 mb-4">
                    <div class="card shadow h-100">
                        <div class="card-header d-flex justify-content-between align-items-center">
                            <span><strong>Order #@order.Id</strong></span>
                            @{
                                var badgeClass = order.Status switch
                                {
                                    "Pending" => "bg-warning text-dark",
                                    "Confirmed" => "bg-info",
                                    "Preparing" => "bg-primary",
                                    "OutForDelivery" => "bg-secondary",
                                    "Delivered" => "bg-success",
                                    "Cancelled" => "bg-danger",
                                    _ => "bg-light text-dark"
                                };
                            }
                            <span class="badge @badgeClass">@order.Status</span>
                        </div>
                        <div class="card-body">
                            <div class="row mb-3">
                                <div class="col-6">
                                    <small class="text-muted">Order Date</small>
                                    <p class="mb-0">@order.OrderDate.ToString("MMM dd, yyyy")</p>
                                    <small>@order.OrderDate.ToString("HH:mm")</small>
                                </div>
                                <div class="col-6 text-end">
                                    <small class="text-muted">Total Amount</small>
                                    <p class="h5 mb-0 text-primary">$@order.TotalAmount.ToString("F2")</p>
                                </div>
                            </div>

                            <hr />

                            <div class="mb-3">
                                <small class="text-muted">Items (@order.OrderItems.Count)</small>
                                <ul class="list-group list-group-flush">
                                    @foreach (var item in order.OrderItems.Take(3))
                                    {
                                        <li class="list-group-item d-flex justify-content-between px-0">
                                            <span>@item.FoodItem?.Name x @item.Quantity</span>
                                            <span class="text-muted">$@((item.Quantity * item.UnitPrice).ToString("F2"))</span>
                                        </li>
                                    }
                                    @if (order.OrderItems.Count > 3)
                                    {
                                        <li class="list-group-item px-0 text-center text-muted">
                                            + @(order.OrderItems.Count - 3) more items
                                        </li>
                                    }
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            }
        </div>
    }
</div>

Comments