Food Ordering System Part 19 | How to Filter Orders by Status and Date in ASP.NET Core MVC


 In Part 19, we are taking our Food Ordering System to a professional level by adding Dynamic Search Logic. This update to the MyOrders method allows users to filter through hundreds of orders by status, specific dates, or price, ensuring a high-performance experience.


Step-by-Step Logic Explanation

  1. Status Filtering: We check if a status is selected. If it isn't "All", we use .Where() to narrow down the results. We also store the selection in ViewBag.CurrentStatus so the dropdown "remembers" what the user picked.

  2. Date Range Logic:

    • From Date: Filters orders that occurred on or after the selected date.

    • To Date: We use .AddDays(1) here. This is a pro-tip! Since DateTime includes time (00:00:00), adding one day ensures we include all orders placed during the final day of the range.

  3. The Switch Expression (Sorting): This is a modern C# feature. It concisely handles multiple sorting scenarios:

    • Newest First (Default): Ensures users see their most recent meal at the top.

    • Price Sorting: Allows users to find their most expensive or cheapest orders easily.

  4. ViewBag.StatusList: We provide a hardcoded list of statuses to the View to populate the dropdown menu dynamically.

ORDERCONTROLLER.CS (PART 19 UPDATED)
public IActionResult MyOrders(string status, string sortOrder, DateTime? fromDate, DateTime? toDate)
{
    // ... (Retrieve UserId and initial Order query from Part 18)

    // Filter by Status
    if (!string.IsNullOrEmpty(status) && status != "All")
    {
        orders = orders.Where(o => o.Status == status);
        ViewBag.CurrentStatus = status;
    }

    // Filter by Date Range
    if (fromDate.HasValue)
    {
        orders = orders.Where(o => o.OrderDate >= fromDate.Value);
        ViewBag.FromDate = fromDate.Value.ToString("yyyy-MM-dd");
    }
    if (toDate.HasValue)
    {
        // Add 1 day to include the entire 'To' date
        orders = orders.Where(o => o.OrderDate <= toDate.Value.AddDays(1));
        ViewBag.ToDate = toDate.Value.ToString("yyyy-MM-dd");
    }

    // Sorting Logic
    ViewBag.CurrentSort = sortOrder;
    orders = sortOrder switch
    {
        "date_asc" => orders.OrderBy(o => o.OrderDate),
        "total_desc" => orders.OrderByDescending(o => o.TotalAmount),
        "total_asc" => orders.OrderBy(o => o.TotalAmount),
        _ => orders.OrderByDescending(o => o.OrderDate) // default: newest first
    };

    ViewBag.StatusList = new List<string> { "All", "Pending", "Confirmed", "Preparing", "OutForDelivery", "Delivered", "Cancelled" };

    return View(orders.ToList());
}

We need to provide the user interface for our new filtering logic. This HTML snippet creates a responsive Search & Filter Bar that sits at the top of the "My Orders" page, allowing customers to interact with the backend code we wrote in the previous step.


Step-by-Step Code Explanation

  • method="get": This is vital for search forms. It puts the search parameters (like status and dates) directly into the URL. This allows users to refresh the page or bookmark a specific search result.

  • asp-action="MyOrders": This Tag Helper ensures the form submits specifically to our MyOrders method in the Controller.

  • Sticky Data (ViewBag): Notice value="@ViewBag.FromDate" and the SelectList. This ensures that after the user clicks "Filter," their selected dates and status stay in the boxes so they don't have to re-type them.

  • Bootstrap Grid (col-md-x): We use a mix of column sizes (3, 2, 2, 3, 2) to ensure the bar looks balanced and professional on widescreen monitors while stacking vertically on mobile.

  • align-items-end: This utility class keeps the "Filter" button perfectly aligned with the bottom of the input boxes, even though those boxes have labels above them.

MYORDERS.CSHTML (FILTER BAR)
@if (TempData["Success"] != null)
{
    <div class="alert alert-success alert-dismissible fade show" role="alert">
        @TempData["Success"]
        <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
    </div>
}

<!-- Filter Section -->
<div class="card shadow-sm mb-4">
    <div class="card-body bg-light">
        <form method="get" asp-action="MyOrders" class="row g-3 align-items-end">
            <div class="col-md-3">
                <label class="form-label">Status</label>
                <select name="status" class="form-select" asp-items="@(new SelectList(ViewBag.StatusList, ViewBag.CurrentStatus))">
                    <option value="All">All Status</option>
                </select>
            </div>
            <div class="col-md-2">
                <label class="form-label">From Date</label>
                <input type="date" name="fromDate" class="form-control" value="@ViewBag.FromDate" />
            </div>
            <div class="col-md-2">
                <label class="form-label">To Date</label>
                <input type="date" name="toDate" class="form-control" value="@ViewBag.ToDate" />
            </div>
            <div class="col-md-3">
                <label class="form-label">Sort By</label>
                <select name="sortOrder" class="form-select">
                    <option value="">Newest First</option>
                    <option value="date_asc" selected="@(ViewBag.CurrentSort == "date_asc")">Oldest First</option>
                    <option value="total_desc" selected="@(ViewBag.CurrentSort == "total_desc")">Highest Amount</option>
                    <option value="total_asc" selected="@(ViewBag.CurrentSort == "total_asc")">Lowest Amount</option>
                </select>
            </div>
            <div class="col-md-2">
                <button type="submit" class="btn btn-primary w-100">
                    <i class="bi bi-funnel"></i> Filter
                </button>
            </div>
        </form>
        @if (!string.IsNullOrEmpty(ViewBag.CurrentStatus) || ViewBag.FromDate != null || ViewBag.ToDate != null)
        {
            <div class="mt-2">
                <a href="/Order/MyOrders" class="btn btn-sm btn-outline-secondary">
                    <i class="bi bi-x-circle"></i> Clear Filters
                </a>
            </div>
        }
    </div>
</div>

Comments