Replace literal Unicode special chars with HTML entities across all 233 views
Sweeps em dashes, en dashes, multiplication signs, ellipses, and curly quotes to their HTML entity equivalents (— – × … ‘ ’) in all .cshtml files, skipping <script> blocks. Prevents encoding corruption from AI tools and Windows encoding mismatches that caused recurring symbol bugs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,196 +0,0 @@
|
||||
@using PowderCoating.Application.DTOs.Common
|
||||
@using PowderCoating.Application.DTOs.Invoice
|
||||
@using PowderCoating.Core.Enums
|
||||
@using PowderCoating.Web.Controllers
|
||||
@model PagedResult<InvoiceListDto>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Invoices";
|
||||
ViewData["PageIcon"] = "bi-receipt";
|
||||
ViewData["PageHelpTitle"] = "Invoices";
|
||||
ViewData["PageHelpContent"] = "Invoices are created from completed jobs and sent to the customer for payment. Lifecycle: Draft (editable) → Sent (locked, awaiting payment) → Partially Paid / Paid. Overdue = past due date with a balance still owed. Outstanding shows the total A/R balance across all unpaid invoices currently on screen. Use Void to cancel without deleting history.";
|
||||
var searchTerm = ViewBag.SearchTerm as string;
|
||||
var statusFilter = ViewBag.StatusFilter as InvoiceStatus?;
|
||||
var outstandingOnly = (bool)(ViewBag.OutstandingOnly ?? false);
|
||||
var thisMonthOnly = (bool)(ViewBag.ThisMonthOnly ?? false);
|
||||
}
|
||||
|
||||
@{
|
||||
var _outstanding = Model.Items.Where(i => i.Status != InvoiceStatus.Paid && i.Status != InvoiceStatus.Voided && i.Status != InvoiceStatus.WrittenOff).Sum(i => i.BalanceDue);
|
||||
var _collected = Model.Items.Sum(i => i.AmountPaid);
|
||||
var _overdue = Model.Items.Count(i => i.IsOverdue);
|
||||
}
|
||||
<div class="pcl-metric-strip">
|
||||
<div class="pcl-metric-strip-cell">
|
||||
@await Html.PartialAsync("_Metric", (Label: "TOTAL", Value: Model.TotalCount.ToString(), Delta: (string?)null, DeltaDir: (string?)null))
|
||||
</div>
|
||||
<div class="pcl-metric-strip-cell">
|
||||
@await Html.PartialAsync("_Metric", (Label: "OUTSTANDING", Value: _outstanding.ToString("C0"), Delta: (string?)null, DeltaDir: (string?)null))
|
||||
</div>
|
||||
<div class="pcl-metric-strip-cell">
|
||||
@await Html.PartialAsync("_Metric", (Label: "OVERDUE", Value: _overdue.ToString(), Delta: (string?)null, DeltaDir: (string?)null))
|
||||
</div>
|
||||
<div class="pcl-metric-strip-cell">
|
||||
@await Html.PartialAsync("_Metric", (Label: "COLLECTED", Value: _collected.ToString("C0"), Delta: (string?)null, DeltaDir: (string?)null))
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-check-circle me-2"></i>@TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>@TempData["Error"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header border-0 py-3">
|
||||
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-start align-items-lg-center gap-3">
|
||||
<form asp-action="Index" method="get" class="d-flex flex-nowrap gap-2 align-items-center">
|
||||
<input type="hidden" name="sortColumn" value="@ViewBag.SortColumn" />
|
||||
<input type="hidden" name="sortDirection" value="@ViewBag.SortDirection" />
|
||||
<input type="hidden" name="pageSize" value="@Model.PageSize" />
|
||||
@if (outstandingOnly) { <input type="hidden" name="outstandingOnly" value="true" /> }
|
||||
@if (thisMonthOnly) { <input type="hidden" name="thisMonthOnly" value="true" /> }
|
||||
<div class="input-group" style="max-width:280px; min-width:180px;">
|
||||
<span class="input-group-text border-end-0"><i class="bi bi-search text-muted"></i></span>
|
||||
<input type="text" name="searchTerm" class="form-control border-start-0"
|
||||
placeholder="Search invoices..." value="@searchTerm">
|
||||
</div>
|
||||
<select class="form-select" name="statusFilter" style="width:auto;">
|
||||
<option value="">All Statuses</option>
|
||||
@foreach (InvoiceStatus s in Enum.GetValues(typeof(InvoiceStatus)))
|
||||
{
|
||||
<option value="@((int)s)" selected="@(statusFilter == s)">@InvoicesController.GetStatusDisplay(s)</option>
|
||||
}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i></button>
|
||||
@if (!string.IsNullOrEmpty(searchTerm) || statusFilter.HasValue || outstandingOnly || thisMonthOnly)
|
||||
{
|
||||
<a asp-action="Index" class="btn btn-outline-secondary">Clear</a>
|
||||
}
|
||||
@if (outstandingOnly)
|
||||
{
|
||||
<span class="badge bg-info text-dark fs-6 fw-normal">
|
||||
<i class="bi bi-funnel-fill me-1"></i>Outstanding A/R
|
||||
</span>
|
||||
}
|
||||
@if (thisMonthOnly && statusFilter == InvoiceStatus.Paid)
|
||||
{
|
||||
<span class="badge bg-success fs-6 fw-normal">
|
||||
<i class="bi bi-funnel-fill me-1"></i>Paid — @DateTime.Now.ToString("MMMM yyyy")
|
||||
</span>
|
||||
}
|
||||
else if (thisMonthOnly)
|
||||
{
|
||||
<span class="badge bg-info text-dark fs-6 fw-normal">
|
||||
<i class="bi bi-funnel-fill me-1"></i>@DateTime.Now.ToString("MMMM yyyy")
|
||||
</span>
|
||||
}
|
||||
</form>
|
||||
<a asp-action="Create" class="btn btn-primary text-nowrap">
|
||||
<i class="bi bi-plus-circle me-2"></i>New Invoice
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if (Model != null && Model.Items.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th sortable="InvoiceNumber" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Invoice #</th>
|
||||
<th>Customer</th>
|
||||
<th>Job #</th>
|
||||
<th sortable="Status" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Status</th>
|
||||
<th sortable="InvoiceDate" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Date</th>
|
||||
<th sortable="DueDate" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Due</th>
|
||||
<th sortable="Total" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection" class="text-end">Total</th>
|
||||
<th sortable="BalanceDue" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection" class="text-end">Balance Due</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var inv in Model.Items)
|
||||
{
|
||||
var rowClass = inv.IsOverdue ? "table-danger" : "";
|
||||
<tr class="@rowClass" style="cursor:pointer;"
|
||||
onclick="window.location='@Url.Action("Details", "Invoices", new { id = inv.Id })'">
|
||||
<td><strong>@inv.InvoiceNumber</strong></td>
|
||||
<td>@inv.CustomerName</td>
|
||||
<td>
|
||||
@if (inv.JobId.HasValue)
|
||||
{
|
||||
<a asp-controller="Jobs" asp-action="Details" asp-route-id="@inv.JobId"
|
||||
class="text-decoration-none" onclick="event.stopPropagation()">
|
||||
@inv.JobNumber
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-success-subtle text-success">Merch</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@await Html.PartialAsync("_StatusChip", (Kind: StatusChipHelper.InvoiceStatus(inv.Status), Text: InvoicesController.GetStatusDisplay(inv.Status)))
|
||||
</td>
|
||||
<td>@inv.InvoiceDate.ToString("MM/dd/yyyy")</td>
|
||||
<td class="@(inv.IsOverdue ? "fw-bold text-danger" : "")">
|
||||
@(inv.DueDate.HasValue ? inv.DueDate.Value.ToString("MM/dd/yyyy") : "—")
|
||||
</td>
|
||||
<td class="text-end">@inv.Total.ToString("C")</td>
|
||||
<td class="text-end @(inv.BalanceDue > 0 ? "fw-semibold" : "text-muted")">
|
||||
@inv.BalanceDue.ToString("C")
|
||||
</td>
|
||||
<td onclick="event.stopPropagation()">
|
||||
<div class="d-flex gap-1">
|
||||
<a asp-action="Details" asp-route-id="@inv.Id"
|
||||
class="btn btn-sm btn-outline-primary" title="View">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a asp-action="DownloadPdf" asp-route-id="@inv.Id"
|
||||
class="btn btn-sm btn-outline-secondary" title="Download PDF">
|
||||
<i class="bi bi-file-pdf"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
@await Html.PartialAsync("_Pagination", Model)
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-receipt" style="font-size:3rem; color:#94a3b8;"></i>
|
||||
<p class="mt-3 text-muted">No invoices found.</p>
|
||||
<a asp-action="Create" class="btn btn-primary mt-2">
|
||||
<i class="bi bi-plus-circle me-2"></i>Create First Invoice
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function changePageSize(size) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('pageSize', size);
|
||||
url.searchParams.set('pageNumber', '1');
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user