littleshop/LittleShop/Areas/Admin/Views/Orders/Index.cshtml
SysAdmin 6e3c11ad75 Add Pending Payment tab and rebrand to TeleShop Admin
- Added new 'Pending Payment' tab to show orders awaiting payment (4 orders)
- Rebranded admin panel from 'LittleShop Admin' to 'TeleShop Admin'
- Updated login page, layout, and dashboard with new branding
- Fixed visibility issue where PendingPayment orders had no tab
- All 13 orders are now visible across appropriate tabs

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 15:55:15 +01:00

419 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@{
ViewData["Title"] = "Order Management";
var orders = ViewData["Orders"] as IEnumerable<LittleShop.DTOs.OrderDto> ?? new List<LittleShop.DTOs.OrderDto>();
var currentTab = ViewData["CurrentTab"] as string ?? "accept";
var tabTitle = ViewData["TabTitle"] as string ?? "Orders";
var pendingCount = (int)(ViewData["PendingCount"] ?? 0);
var acceptCount = (int)(ViewData["AcceptCount"] ?? 0);
var packingCount = (int)(ViewData["PackingCount"] ?? 0);
var dispatchedCount = (int)(ViewData["DispatchedCount"] ?? 0);
var onHoldCount = (int)(ViewData["OnHoldCount"] ?? 0);
}
<div class="row mb-3">
<div class="col">
<h1 class="h3"><i class="fas fa-clipboard-list"></i> <span class="d-none d-md-inline">Order Management</span><span class="d-md-none">Orders</span></h1>
<p class="text-muted d-none d-md-block">Workflow-focused order fulfillment system</p>
</div>
<div class="col-auto">
<a href="@Url.Action("Create")" class="btn btn-primary btn-sm">
<i class="fas fa-plus"></i> <span class="d-none d-sm-inline">Create Order</span><span class="d-sm-none">New</span>
</a>
</div>
</div>
<!-- Workflow Tabs - Mobile Responsive -->
<ul class="nav nav-tabs mb-3 flex-nowrap overflow-auto" id="orderTabs" role="tablist" style="white-space: nowrap;">
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "pending" ? "active" : "")" href="@Url.Action("Index", new { tab = "pending" })">
<i class="fas fa-clock"></i>
<span class="d-none d-md-inline">Pending Payment</span>
<span class="d-md-none">Pending</span>
@if (pendingCount > 0)
{
<span class="badge bg-secondary ms-1">@pendingCount</span>
}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "accept" ? "active" : "")" href="@Url.Action("Index", new { tab = "accept" })">
<i class="fas fa-check-circle"></i>
<span class="d-none d-md-inline">Accept Orders</span>
<span class="d-md-none">Accept</span>
@if (acceptCount > 0)
{
<span class="badge bg-danger ms-1">@acceptCount</span>
}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "packing" ? "active" : "")" href="@Url.Action("Index", new { tab = "packing" })">
<i class="fas fa-box"></i>
<span class="d-none d-md-inline">Packing</span>
<span class="d-md-none">Pack</span>
@if (packingCount > 0)
{
<span class="badge bg-warning ms-1">@packingCount</span>
}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "dispatched" ? "active" : "")" href="@Url.Action("Index", new { tab = "dispatched" })">
<i class="fas fa-shipping-fast"></i>
<span class="d-none d-md-inline">Dispatched</span>
<span class="d-md-none">Ship</span>
@if (dispatchedCount > 0)
{
<span class="badge bg-info ms-1">@dispatchedCount</span>
}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "delivered" ? "active" : "")" href="@Url.Action("Index", new { tab = "delivered" })">
<i class="fas fa-check"></i>
<span class="d-none d-md-inline">Delivered</span>
<span class="d-md-none">Done</span>
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "onhold" ? "active" : "")" href="@Url.Action("Index", new { tab = "onhold" })">
<i class="fas fa-pause-circle"></i>
<span class="d-none d-md-inline">On Hold</span>
<span class="d-md-none">Hold</span>
@if (onHoldCount > 0)
{
<span class="badge bg-secondary ms-1">@onHoldCount</span>
}
</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link @(currentTab == "cancelled" ? "active" : "")" href="@Url.Action("Index", new { tab = "cancelled" })">
<i class="fas fa-times-circle"></i>
<span class="d-none d-md-inline">Cancelled</span>
<span class="d-md-none">Cancel</span>
</a>
</li>
</ul>
<div class="card">
<div class="card-header">
<h5 class="mb-0">@tabTitle (@orders.Count())</h5>
</div>
<div class="card-body">
@if (orders.Any())
{
<!-- Desktop Table View (hidden on mobile) -->
<div class="table-responsive d-none d-lg-block">
<table class="table table-hover">
<thead>
<tr>
<th>Order ID</th>
<th>Customer</th>
<th>Items</th>
<th>Total</th>
<th>Status</th>
<th>Timeline</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var order in orders)
{
<tr>
<td>
<strong>#@order.Id.ToString().Substring(0, 8)</strong>
<br><small class="text-muted">@order.CreatedAt.ToString("MMM dd, HH:mm")</small>
</td>
<td>
@if (order.Customer != null)
{
<strong>@order.Customer.DisplayName</strong>
<br><small class="text-muted">@order.Customer.CustomerType</small>
}
else
{
<strong>@order.ShippingName</strong>
<br><small class="text-muted">Anonymous</small>
}
</td>
<td>
@foreach (var item in order.Items.Take(2))
{
<div>@item.Quantity× @item.ProductName</div>
@if (!string.IsNullOrEmpty(item.ProductMultiBuyName))
{
<small class="text-muted">(@item.ProductMultiBuyName)</small>
}
}
@if (order.Items.Count > 2)
{
<small class="text-muted">+@(order.Items.Count - 2) more...</small>
}
</td>
<td>
<strong>£@order.TotalAmount</strong>
<br><small class="text-muted">@order.Currency</small>
</td>
<td>
@{
var statusClass = order.Status switch
{
LittleShop.Enums.OrderStatus.PendingPayment => "bg-warning",
LittleShop.Enums.OrderStatus.PaymentReceived => "bg-info",
LittleShop.Enums.OrderStatus.Accepted => "bg-primary",
LittleShop.Enums.OrderStatus.Packing => "bg-warning",
LittleShop.Enums.OrderStatus.Dispatched => "bg-info",
LittleShop.Enums.OrderStatus.Delivered => "bg-success",
LittleShop.Enums.OrderStatus.OnHold => "bg-secondary",
LittleShop.Enums.OrderStatus.Cancelled => "bg-danger",
_ => "bg-light"
};
}
<span class="badge @statusClass">@order.Status</span>
@if (!string.IsNullOrEmpty(order.TrackingNumber))
{
<br><small class="text-muted">@order.TrackingNumber</small>
}
</td>
<td>
<small>
@if (order.AcceptedAt.HasValue)
{
<div>✅ Accepted @order.AcceptedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.PackingStartedAt.HasValue)
{
<div>📦 Packing @order.PackingStartedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.DispatchedAt.HasValue)
{
<div>🚚 Dispatched @order.DispatchedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.ExpectedDeliveryDate.HasValue)
{
<div class="text-muted">📅 Expected @order.ExpectedDeliveryDate.Value.ToString("MMM dd")</div>
}
@if (order.OnHoldAt.HasValue)
{
<div class="text-warning">⏸️ On Hold: @order.OnHoldReason</div>
}
</small>
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="@Url.Action("Details", new { id = order.Id })" class="btn btn-outline-primary" title="View Details">
<i class="fas fa-eye"></i>
</a>
@* Workflow-specific actions *@
@if (order.Status == LittleShop.Enums.OrderStatus.PaymentReceived)
{
<form method="post" action="@Url.Action("AcceptOrder", new { id = order.Id })" class="d-inline">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-success btn-sm" title="Accept Order">
<i class="fas fa-check"></i>
</button>
</form>
}
@if (order.Status == LittleShop.Enums.OrderStatus.Accepted)
{
<form method="post" action="@Url.Action("StartPacking", new { id = order.Id })" class="d-inline">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-warning btn-sm" title="Start Packing">
<i class="fas fa-box"></i>
</button>
</form>
}
@if (order.Status != LittleShop.Enums.OrderStatus.OnHold && order.Status != LittleShop.Enums.OrderStatus.Delivered && order.Status != LittleShop.Enums.OrderStatus.Cancelled)
{
<button type="button" class="btn btn-secondary btn-sm" title="Put On Hold" data-bs-toggle="modal" data-bs-target="#holdModal-@order.Id">
<i class="fas fa-pause"></i>
</button>
}
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
<!-- Mobile Card View (hidden on desktop) -->
<div class="d-lg-none">
@foreach (var order in orders)
{
<div class="card mb-3 border-start border-3 @(order.Status switch {
LittleShop.Enums.OrderStatus.PaymentReceived => "border-warning",
LittleShop.Enums.OrderStatus.Accepted => "border-primary",
LittleShop.Enums.OrderStatus.Packing => "border-info",
LittleShop.Enums.OrderStatus.Dispatched => "border-success",
LittleShop.Enums.OrderStatus.OnHold => "border-secondary",
_ => "border-light"
})">
<div class="card-body">
<div class="row align-items-center">
<div class="col">
<h6 class="card-title mb-1">
<strong>#@order.Id.ToString().Substring(0, 8)</strong>
<span class="badge @(order.Status switch {
LittleShop.Enums.OrderStatus.PendingPayment => "bg-warning",
LittleShop.Enums.OrderStatus.PaymentReceived => "bg-info",
LittleShop.Enums.OrderStatus.Accepted => "bg-primary",
LittleShop.Enums.OrderStatus.Packing => "bg-warning",
LittleShop.Enums.OrderStatus.Dispatched => "bg-info",
LittleShop.Enums.OrderStatus.Delivered => "bg-success",
LittleShop.Enums.OrderStatus.OnHold => "bg-secondary",
LittleShop.Enums.OrderStatus.Cancelled => "bg-danger",
_ => "bg-light"
}) ms-2">@order.Status</span>
</h6>
<div class="small text-muted mb-2">
@if (order.Customer != null)
{
<text><strong>@order.Customer.DisplayName</strong> - @order.Customer.CustomerType</text>
}
else
{
<text><strong>@order.ShippingName</strong> - Anonymous</text>
}
</div>
<div class="small mb-2">
<strong>£@order.TotalAmount</strong>
@if (order.Items.Any())
{
var firstItem = order.Items.First();
<text> - @firstItem.Quantity x @firstItem.ProductName</text>
@if (!string.IsNullOrEmpty(firstItem.ProductMultiBuyName))
{
<span class="text-muted">(@firstItem.ProductMultiBuyName)</span>
}
@if (order.Items.Count > 1)
{
<span class="text-muted"> +@(order.Items.Count - 1) more</span>
}
}
</div>
@if (!string.IsNullOrEmpty(order.TrackingNumber))
{
<div class="small text-muted">
📦 @order.TrackingNumber
</div>
}
<!-- Timeline for mobile -->
<div class="small text-muted mt-2">
@if (order.AcceptedAt.HasValue)
{
<div>✅ @order.AcceptedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.PackingStartedAt.HasValue)
{
<div>📦 @order.PackingStartedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.DispatchedAt.HasValue)
{
<div>🚚 @order.DispatchedAt.Value.ToString("MMM dd, HH:mm")</div>
}
@if (order.ExpectedDeliveryDate.HasValue)
{
<div>📅 Expected @order.ExpectedDeliveryDate.Value.ToString("MMM dd")</div>
}
@if (order.OnHoldAt.HasValue)
{
<div class="text-warning">⏸️ On Hold: @order.OnHoldReason</div>
}
</div>
</div>
<div class="col-auto">
<!-- Mobile Action Buttons -->
<div class="d-grid gap-1" style="min-width: 100px;">
<a href="@Url.Action("Details", new { id = order.Id })" class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye"></i> View
</a>
@if (order.Status == LittleShop.Enums.OrderStatus.PaymentReceived)
{
<form method="post" action="@Url.Action("AcceptOrder", new { id = order.Id })">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-success btn-sm w-100">
<i class="fas fa-check"></i> Accept
</button>
</form>
}
@if (order.Status == LittleShop.Enums.OrderStatus.Accepted)
{
<form method="post" action="@Url.Action("StartPacking", new { id = order.Id })">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-warning btn-sm w-100">
<i class="fas fa-box"></i> Pack
</button>
</form>
}
@if (order.Status != LittleShop.Enums.OrderStatus.OnHold && order.Status != LittleShop.Enums.OrderStatus.Delivered && order.Status != LittleShop.Enums.OrderStatus.Cancelled)
{
<button type="button" class="btn btn-secondary btn-sm w-100" data-bs-toggle="modal" data-bs-target="#holdModal-@order.Id">
<i class="fas fa-pause"></i> Hold
</button>
}
</div>
</div>
</div>
</div>
</div>
}
</div>
}
else
{
<div class="text-center py-4">
<i class="fas fa-clipboard-list fa-3x text-muted mb-3"></i>
<p class="text-muted">No orders found in this category.</p>
@if (currentTab == "accept")
{
<p class="text-muted">Orders will appear here when payment is received.</p>
}
</div>
}
</div>
</div>
@* Hold Modals for each order *@
@foreach (var order in orders.Where(o => o.Status != LittleShop.Enums.OrderStatus.OnHold && o.Status != LittleShop.Enums.OrderStatus.Delivered && o.Status != LittleShop.Enums.OrderStatus.Cancelled))
{
<div class="modal fade" id="holdModal-@order.Id" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="@Url.Action("PutOnHold", new { id = order.Id })">
@Html.AntiForgeryToken()
<div class="modal-header">
<h5 class="modal-title">Put Order On Hold</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="reason-@order.Id" class="form-label">Reason for Hold</label>
<input name="reason" id="reason-@order.Id" class="form-control" placeholder="e.g., Awaiting stock, Customer query" required />
</div>
<div class="mb-3">
<label for="notes-@order.Id" class="form-label">Additional Notes</label>
<textarea name="notes" id="notes-@order.Id" class="form-control" rows="2" placeholder="Optional additional details..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-warning">Put On Hold</button>
</div>
</form>
</div>
</div>
</div>
}