Button consistency sweep + mobile responsiveness patches

- Standardize modal dismiss/cancel buttons to btn-outline-secondary across 70+ views
- Remove btn-sm from page-level Create and Back buttons (Index + Detail pages)
- Fix Edit buttons on Details pages: btn-secondary -> btn-warning
- Fix form Cancel/Back links: btn-secondary -> btn-outline-secondary
- Add 10 CSS patches to site.css for mobile/tablet responsiveness:
  top-navbar overflow prevention, page-header flex-wrap at 575px,
  table action button min-height override, notification dropdown width cap,
  tablet content padding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 19:04:10 -04:00
parent 328b195127
commit e2f9e9ae4f
71 changed files with 553 additions and 422 deletions
@@ -1,4 +1,4 @@
@using PowderCoating.Web.Controllers
@using PowderCoating.Web.Controllers
@model List<EntityPurgeStat>
@{
ViewData["Title"] = "Data Purge & Cleanup";
@@ -59,7 +59,7 @@
<div class="alert alert-warning alert-permanent d-flex gap-3 align-items-start mb-3">
<i class="bi bi-exclamation-triangle-fill fs-4 flex-shrink-0 mt-1"></i>
<div>
<strong>Destructive operation — this cannot be undone.</strong>
<strong>Destructive operation this cannot be undone.</strong>
Purging permanently deletes records from the database. Soft-deleted records are hidden from users but still occupy database space. Use this tool periodically to reclaim space and keep the database clean.
Job photo blobs in Azure Storage are also deleted when purging job photo records.
</div>
@@ -83,8 +83,8 @@
<th style="width:36px"></th>
<th>Entity</th>
<th class="text-end" style="width:90px">Total</th>
<th class="text-end" style="width:100px">0–30d</th>
<th class="text-end" style="width:100px">30–90d</th>
<th class="text-end" style="width:100px">030d</th>
<th class="text-end" style="width:100px">3090d</th>
<th class="text-end" style="width:100px">&gt;90d</th>
<th style="width:130px">Oldest</th>
<th style="width:42px">
@@ -109,7 +109,7 @@
}
else
{
<span class="text-muted">—</span>
<span class="text-muted"></span>
}
</td>
<td class="text-end">
@@ -117,24 +117,24 @@
{
<span class="badge bg-success-subtle text-success">@s.DeletedLast30Days</span>
}
else { <span class="text-muted">—</span> }
else { <span class="text-muted"></span> }
</td>
<td class="text-end">
@if (s.Deleted30To90Days > 0)
{
<span class="badge bg-warning-subtle text-warning">@s.Deleted30To90Days</span>
}
else { <span class="text-muted">—</span> }
else { <span class="text-muted"></span> }
</td>
<td class="text-end">
@if (s.DeletedOlderThan90Days > 0)
{
<span class="badge bg-danger-subtle text-danger">@s.DeletedOlderThan90Days</span>
}
else { <span class="text-muted">—</span> }
else { <span class="text-muted"></span> }
</td>
<td class="text-muted">
@(s.OldestDeletion.HasValue ? s.OldestDeletion.Value.ToString("MM/dd/yyyy") : "—")
@(s.OldestDeletion.HasValue ? s.OldestDeletion.Value.ToString("MM/dd/yyyy") : "")
</td>
<td class="text-center">
<input type="checkbox" class="form-check-input entity-select"
@@ -149,7 +149,7 @@
</table>
</div>
<!-- Mobile card view for this group — shown on screens < 992px -->
<!-- Mobile card view for this group shown on screens < 992px -->
<div class="mobile-card-view">
<div class="px-3 pt-2 pb-1">
<span class="text-muted text-uppercase fw-semibold" style="font-size:0.7rem;letter-spacing:.05em">@group.Key</span>
@@ -164,7 +164,7 @@
</div>
<div class="mobile-card-title">
<h6>@s.Label</h6>
<small>Oldest: @(s.OldestDeletion.HasValue ? s.OldestDeletion.Value.ToString("MM/dd/yyyy") : "—")</small>
<small>Oldest: @(s.OldestDeletion.HasValue ? s.OldestDeletion.Value.ToString("MM/dd/yyyy") : "")</small>
</div>
</div>
<div class="mobile-card-body">
@@ -175,11 +175,11 @@
{
<span class="badge bg-secondary">@s.Total</span>
}
else { <span class="text-muted">—</span> }
else { <span class="text-muted"></span> }
</span>
</div>
<div class="mobile-card-row">
<span class="mobile-card-label">0–30d / 30–90d / &gt;90d</span>
<span class="mobile-card-label">030d / 3090d / &gt;90d</span>
<span class="mobile-card-value">@s.DeletedLast30Days / @s.Deleted30To90Days / @s.DeletedOlderThan90Days</span>
</div>
</div>
@@ -279,7 +279,7 @@
<p class="mb-0 text-muted">Are you sure you want to continue?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirmPurgeBtn">
<i class="bi bi-trash3-fill me-2"></i>Yes, Purge Permanently
</button>
@@ -300,7 +300,7 @@
const confirmModal = new bootstrap.Modal(document.getElementById('confirmModal'));
const confirmSummary= document.getElementById('confirmSummary');
// ── Select all ──────────────────────────────────────────────────────────
// ── Select all ──────────────────────────────────────────────────────────
selectAll.addEventListener('change', () => {
document.querySelectorAll('.entity-select:not(:disabled)').forEach(cb => {
cb.checked = selectAll.checked;
@@ -308,7 +308,7 @@
updatePurgeBtn();
});
// ── Group select all ────────────────────────────────────────────────────
// ── Group select all ────────────────────────────────────────────────────
document.querySelectorAll('.group-select-all').forEach(ga => {
ga.addEventListener('change', () => {
document.querySelectorAll(`.entity-select[data-group="${ga.dataset.group}"]:not(:disabled)`)
@@ -335,7 +335,7 @@
previewRes.classList.add('d-none');
}
// ── Preview ─────────────────────────────────────────────────────────────
// ── Preview ─────────────────────────────────────────────────────────────
previewBtn.addEventListener('click', async () => {
const entities = getSelectedEntities();
if (!entities.length) {
@@ -344,7 +344,7 @@
}
previewBtn.disabled = true;
previewBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading…';
previewBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Loading';
const days = document.getElementById('olderThanDays').value;
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;
@@ -379,7 +379,7 @@
}
});
// ── Purge button → modal ────────────────────────────────────────────────
// ── Purge button modal ────────────────────────────────────────────────
purgeBtn.addEventListener('click', () => {
const entities = getSelectedEntities();
const days = document.getElementById('olderThanDays').value;
@@ -390,7 +390,7 @@
confirmModal.show();
});
// ── Confirm → submit form ───────────────────────────────────────────────
// ── Confirm submit form ───────────────────────────────────────────────
document.getElementById('confirmPurgeBtn').addEventListener('click', () => {
const entities = getSelectedEntities();
const days = document.getElementById('olderThanDays').value;