Jobs list defaults to On Floor; add Completed filter pill; fix encoding bugs
- /Jobs now redirects to ?statusGroup=active so completed jobs don't clutter the default view - Add Completed pill (filters Completed + ReadyForPickup + Delivered) - Pill badge counts are now global DB counts, not page-local item counts - Ready pill badge now shows ReadyForPickup-only count - All pill links to ?statusGroup=all to bypass the redirect - Fix double-encoded & in Completed filter alert label - Fix corrupted em dash (â€") in Customers/Details billing email fallback text Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,11 @@ public class JobsController : Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
// Default landing view: On Floor — redirect bare /Jobs to ?statusGroup=active
|
||||
// so completed/cancelled jobs don't clutter the first screen.
|
||||
if (string.IsNullOrEmpty(statusGroup) && string.IsNullOrEmpty(searchTerm) && string.IsNullOrEmpty(tagFilter))
|
||||
return RedirectToAction("Index", new { statusGroup = "active" });
|
||||
|
||||
// Create and validate grid request
|
||||
var gridRequest = new GridRequest
|
||||
{
|
||||
@@ -141,6 +146,13 @@ public class JobsController : Controller
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled;
|
||||
}
|
||||
else if (statusGroup == "completed")
|
||||
{
|
||||
filter = j => j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Completed
|
||||
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup
|
||||
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Delivered;
|
||||
}
|
||||
// "all" or unknown group: no filter applied (show every status)
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
@@ -195,6 +207,27 @@ public class JobsController : Controller
|
||||
gridRequest, jobDtos,
|
||||
string.IsNullOrWhiteSpace(tagFilter) ? totalCount : jobDtos.Count);
|
||||
|
||||
// Pill badge counts — always global (not scoped to current filter/page)
|
||||
var today = DateTime.Today;
|
||||
ViewBag.AllJobCount = await _unitOfWork.Jobs.CountAsync();
|
||||
ViewBag.ActiveCount = await _unitOfWork.Jobs.CountAsync(j =>
|
||||
j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
|
||||
ViewBag.OverdueCount = await _unitOfWork.Jobs.CountAsync(j =>
|
||||
j.DueDate < today
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Completed
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.ReadyForPickup
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Delivered
|
||||
&& j.JobStatus.StatusCode != AppConstants.StatusCodes.Job.Cancelled);
|
||||
ViewBag.CompletedCount = await _unitOfWork.Jobs.CountAsync(j =>
|
||||
j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Completed
|
||||
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup
|
||||
|| j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.Delivered);
|
||||
ViewBag.ReadyCount = await _unitOfWork.Jobs.CountAsync(j =>
|
||||
j.JobStatus.StatusCode == AppConstants.StatusCodes.Job.ReadyForPickup);
|
||||
|
||||
// Set ViewBag for sorting
|
||||
ViewBag.SearchTerm = searchTerm;
|
||||
ViewBag.StatusGroup = statusGroup;
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">Not set — invoices go to contact email</span>
|
||||
<span class=”text-muted”>Not set — invoices go to contact email</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
}
|
||||
|
||||
@{
|
||||
var _wip = Model.Items.Count(j => j.StatusIsWIP);
|
||||
var _done = Model.Items.Count(j => j.StatusCode == "COMPLETED" || j.StatusCode == "READYFORPICKUP" || j.StatusCode == "DELIVERED");
|
||||
var _overdue = Model.Items.Count(j => j.DueDate.HasValue && j.DueDate.Value < DateTime.Now && j.StatusCode != "COMPLETED" && j.StatusCode != "READYFORPICKUP" && j.StatusCode != "DELIVERED" && j.StatusCode != "CANCELLED");
|
||||
var _value = Model.Items.Sum(j => j.FinalPrice);
|
||||
var _allCount = (int)(ViewBag.AllJobCount ?? 0);
|
||||
var _wip = (int)(ViewBag.ActiveCount ?? 0);
|
||||
var _done = (int)(ViewBag.CompletedCount ?? 0);
|
||||
var _ready = (int)(ViewBag.ReadyCount ?? 0);
|
||||
var _overdue = (int)(ViewBag.OverdueCount ?? 0);
|
||||
var _value = Model.Items.Sum(j => j.FinalPrice);
|
||||
}
|
||||
<div class="pcl-metric-strip">
|
||||
<div class="pcl-metric-strip-cell">
|
||||
@@ -39,23 +41,23 @@
|
||||
Showing <strong>@Model.TotalCount</strong> job(s) matching "<strong>@ViewBag.SearchTerm</strong>"
|
||||
<small class="text-muted ms-2">(searches job number, description, customer, PO, instructions, status, priority)</small>
|
||||
</div>
|
||||
<a href="@Url.Action("Index")" class="btn btn-sm btn-outline-secondary">
|
||||
<a href="@Url.Action("Index", new { statusGroup = "active" })" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-x me-1"></i>Clear Filter
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(ViewBag.StatusGroup as string))
|
||||
@if (!string.IsNullOrEmpty(ViewBag.StatusGroup as string) && ViewBag.StatusGroup != "active" && ViewBag.StatusGroup != "all")
|
||||
{
|
||||
var groupLabel = ViewBag.StatusGroup == "active" ? "Active Jobs (excluding completed & cancelled)"
|
||||
: ViewBag.StatusGroup == "overdue" ? "Overdue Jobs (past due date)"
|
||||
: ViewBag.StatusGroup;
|
||||
var groupLabel = ViewBag.StatusGroup == "overdue" ? "Overdue Jobs (past due date)"
|
||||
: ViewBag.StatusGroup == "completed" ? "Completed Jobs (completed, ready for pickup & delivered)"
|
||||
: (string)ViewBag.StatusGroup;
|
||||
<div class="alert alert-warning alert-permanent d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<i class="bi bi-funnel-fill me-2"></i>
|
||||
Showing: <strong>@groupLabel</strong> — @Model.TotalCount result@(Model.TotalCount == 1 ? "" : "s")
|
||||
</div>
|
||||
<a href="@Url.Action("Index")" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-x me-1"></i>Show All
|
||||
<a href="@Url.Action("Index", new { statusGroup = "active" })" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-x me-1"></i>Back to On Floor
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
@@ -63,7 +65,8 @@
|
||||
@{
|
||||
var _activeGroup = ViewBag.StatusGroup as string;
|
||||
var _activeSearch = ViewBag.SearchTerm as string;
|
||||
var _noFilter = string.IsNullOrEmpty(_activeGroup) && string.IsNullOrEmpty(_activeSearch) && string.IsNullOrEmpty(ViewBag.TagFilter as string);
|
||||
// "all" is the explicit show-everything group (bare URL now redirects to "active")
|
||||
var _noFilter = _activeGroup == "all" && string.IsNullOrEmpty(_activeSearch) && string.IsNullOrEmpty(ViewBag.TagFilter as string);
|
||||
}
|
||||
<!-- Jobs Table Card -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
@@ -135,8 +138,8 @@
|
||||
</div>
|
||||
<!-- Row 2: quick-view pills -->
|
||||
<div class="pcl-pill-group">
|
||||
<a href="@Url.Action("Index")" class="pcl-pill @(_noFilter ? "active" : "")">
|
||||
All <span class="pcl-pill-count">@Model.TotalCount</span>
|
||||
<a href="@Url.Action("Index", new { statusGroup = "all" })" class="pcl-pill @(_noFilter ? "active" : "")">
|
||||
All <span class="pcl-pill-count">@_allCount</span>
|
||||
</a>
|
||||
<a href="@Url.Action("Index", new { statusGroup = "active" })" class="pcl-pill @(_activeGroup == "active" ? "active" : "")">
|
||||
On floor <span class="pcl-pill-count">@_wip</span>
|
||||
@@ -145,7 +148,10 @@
|
||||
Overdue <span class="pcl-pill-count">@_overdue</span>
|
||||
</a>
|
||||
<a href="@Url.Action("Index", new { searchTerm = "ReadyForPickup" })" class="pcl-pill @(_activeSearch == "ReadyForPickup" ? "active" : "")">
|
||||
Ready <span class="pcl-pill-count">@_done</span>
|
||||
Ready <span class="pcl-pill-count">@_ready</span>
|
||||
</a>
|
||||
<a href="@Url.Action("Index", new { statusGroup = "completed" })" class="pcl-pill @(_activeGroup == "completed" ? "active" : "")">
|
||||
Completed <span class="pcl-pill-count">@_done</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user