Design consistency audit fixes: alerts, cards, dark mode, utilities
Alert sweep (113 alerts, 79 files):
All persistent static banners now carry alert-permanent so the
layout's 5-second auto-dismiss cannot swallow guidance, warnings,
or validation errors. Transient dismissible toasts left untouched.
CSS fixes (site.css):
.card.shadow-sm — strips rogue border from ~40 drifted cards
.card-header.bg-white — rebinds to var(--bs-body-bg) so card
headers follow dark/light theme correctly
Typography utilities — .text-2xs (.68rem), .text-xs (.73rem)
Token color classes — .text-ember, .text-ok, .text-bad,
.text-warn, .text-cool, .bg-paper-2
Layout utilities — .mw-xs/sm/md/lg replace inline max-width
Comment — documents text-ember vs text-primary intent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
@using PowderCoating.Web.Controllers
|
||||
@using PowderCoating.Web.Controllers
|
||||
@model List<EntityPurgeStat>
|
||||
@{
|
||||
ViewData["Title"] = "Data Purge & Cleanup";
|
||||
@@ -56,10 +56,10 @@
|
||||
}
|
||||
|
||||
@* Warning banner *@
|
||||
<div class="alert alert-warning d-flex gap-3 align-items-start mb-3">
|
||||
<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">0–30d</th>
|
||||
<th class="text-end" style="width:100px">30–90d</th>
|
||||
<th class="text-end" style="width:100px">>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 / >90d</span>
|
||||
<span class="mobile-card-label">0–30d / 30–90d / >90d</span>
|
||||
<span class="mobile-card-value">@s.DeletedLast30Days / @s.Deleted30To90Days / @s.DeletedOlderThan90Days</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user