a0bdd2b5b4
Replace all corruption variants with HTML entities across 226 view files: - 3-char UTF-8-as-Win1252 sequences (ae-corruption) - Standalone smart/curly quotes that break C# Razor expressions - Partially re-corrupted variants where the 3rd byte was normalised to ASCII tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the script itself never contains a literal non-ASCII character; supports -DryRun .githooks/pre-commit: blocks commits containing the ae-corruption byte signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the hook is repo-committed and active for all future work on this machine. Build clean; 225 unit tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
171 lines
7.9 KiB
Plaintext
171 lines
7.9 KiB
Plaintext
@using PowderCoating.Web.Controllers
|
|
@model AdminEmailSelectionModel
|
|
@{
|
|
ViewData["Title"] = "Choose Companies";
|
|
}
|
|
|
|
<div class="container-fluid py-4" style="max-width:1100px">
|
|
<div class="d-flex flex-wrap justify-content-between align-items-center gap-3 mb-4">
|
|
<div>
|
|
<h3 class="mb-1"><i class="bi bi-building-check me-2 text-primary"></i>Admin Email Wizard</h3>
|
|
<p class="text-muted mb-0">Step 2 of 3: choose which companies should receive this message.</p>
|
|
</div>
|
|
<div class="badge text-bg-secondary px-3 py-2">@Model.AvailableCompanies.Count company records</div>
|
|
</div>
|
|
|
|
<div class="card shadow-sm border-0 mb-4">
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-lg-6">
|
|
<div class="small text-muted text-uppercase fw-semibold mb-1">Subject</div>
|
|
<div class="fw-semibold">@Model.Subject</div>
|
|
</div>
|
|
<div class="col-lg-6">
|
|
<div class="small text-muted text-uppercase fw-semibold mb-1">Message Summary</div>
|
|
<div class="text-muted">Rich-text message prepared. Merge tokens will render on the preview step.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" asp-action="Preview" id="company-select-form">
|
|
@Html.AntiForgeryToken()
|
|
<input type="hidden" asp-for="Subject" />
|
|
<input type="hidden" asp-for="BodyHtml" />
|
|
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-body p-4">
|
|
<div class="row g-3 align-items-center mb-3">
|
|
<div class="col-lg-5">
|
|
<input type="search" class="form-control" id="company-filter" placeholder="Search company, contact, or email" />
|
|
</div>
|
|
<div class="col-lg-7 d-flex flex-wrap gap-2 justify-content-lg-end">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="select-all-btn">Select All Visible</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="clear-all-btn">Clear Visible</button>
|
|
<span class="badge text-bg-primary" id="selected-count">0 selected</span>
|
|
</div>
|
|
</div>
|
|
|
|
<span asp-validation-for="CompanyIds" class="text-danger small d-block mb-3"></span>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width:56px"></th>
|
|
<th>Company</th>
|
|
<th>Primary Contact</th>
|
|
<th>Email</th>
|
|
<th>Company Admin</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var company in Model.AvailableCompanies)
|
|
{
|
|
<tr class="company-row">
|
|
<td>
|
|
<input class="form-check-input company-checkbox"
|
|
type="checkbox"
|
|
name="CompanyIds"
|
|
value="@company.CompanyId"
|
|
@(company.IsSelected ? "checked" : null) />
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold">@company.CompanyName</div>
|
|
<div class="small text-muted">#@company.CompanyId</div>
|
|
</td>
|
|
<td>@(string.IsNullOrWhiteSpace(company.PrimaryContactName) ? "—" : company.PrimaryContactName)</td>
|
|
<td>
|
|
@if (string.IsNullOrWhiteSpace(company.PrimaryContactEmail))
|
|
{
|
|
<span class="badge text-bg-warning">Missing</span>
|
|
}
|
|
else
|
|
{
|
|
<span>@company.PrimaryContactEmail</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
<div>@(string.IsNullOrWhiteSpace(company.CompanyAdminName) ? "—" : company.CompanyAdminName)</div>
|
|
@if (!string.IsNullOrWhiteSpace(company.CompanyAdminEmail))
|
|
{
|
|
<div class="small text-muted">@company.CompanyAdminEmail</div>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (company.IsActive)
|
|
{
|
|
<span class="badge text-bg-success">Active</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="badge text-bg-secondary">Inactive</span>
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="d-flex flex-wrap justify-content-between gap-3 mt-4">
|
|
<button type="submit"
|
|
formaction="@Url.Action("BackToCompose")"
|
|
class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-2"></i>Back to Compose
|
|
</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
Next: Preview <i class="bi bi-arrow-right ms-2"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<partial name="_ValidationScriptsPartial" />
|
|
<script>
|
|
(function () {
|
|
const filterInput = document.getElementById('company-filter');
|
|
const rows = Array.from(document.querySelectorAll('.company-row'));
|
|
const checkboxes = Array.from(document.querySelectorAll('.company-checkbox'));
|
|
const selectedCount = document.getElementById('selected-count');
|
|
|
|
function updateSelectedCount() {
|
|
const total = checkboxes.filter(cb => cb.checked).length;
|
|
selectedCount.textContent = `${total} selected`;
|
|
}
|
|
|
|
function applyFilter() {
|
|
const term = filterInput.value.trim().toLowerCase();
|
|
rows.forEach(row => {
|
|
const visible = row.textContent.toLowerCase().includes(term);
|
|
row.style.display = visible ? '' : 'none';
|
|
});
|
|
}
|
|
|
|
document.getElementById('select-all-btn').addEventListener('click', () => {
|
|
rows.forEach(row => {
|
|
if (row.style.display === 'none') return;
|
|
row.querySelector('.company-checkbox').checked = true;
|
|
});
|
|
updateSelectedCount();
|
|
});
|
|
|
|
document.getElementById('clear-all-btn').addEventListener('click', () => {
|
|
rows.forEach(row => {
|
|
if (row.style.display === 'none') return;
|
|
row.querySelector('.company-checkbox').checked = false;
|
|
});
|
|
updateSelectedCount();
|
|
});
|
|
|
|
filterInput.addEventListener('input', applyFilter);
|
|
checkboxes.forEach(cb => cb.addEventListener('change', updateSelectedCount));
|
|
updateSelectedCount();
|
|
})();
|
|
</script>
|
|
}
|