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:
@@ -1,4 +1,4 @@
|
||||
@model PowderCoating.Application.DTOs.Dashboard.DashboardViewModel
|
||||
@model PowderCoating.Application.DTOs.Dashboard.DashboardViewModel
|
||||
@using Microsoft.AspNetCore.Html
|
||||
@using PowderCoating.Application.DTOs.Health
|
||||
@using PowderCoating.Web.ViewModels.Dashboard
|
||||
@@ -24,7 +24,7 @@
|
||||
<p class="mb-3" style="font-family:var(--font-display);font-size:1.35rem;font-weight:500;line-height:1.4;color:var(--pcl-ink);">
|
||||
@if (_attnCount > 0)
|
||||
{
|
||||
<span>Shop is </span><span style="color:var(--pcl-bad);">running hot</span><span> — @_attnCount item@(_attnCount == 1 ? "" : "s") need attention.</span>
|
||||
<span>Shop is </span><span style="color:var(--pcl-bad);">running hot</span><span> — @_attnCount item@(_attnCount == 1 ? "" : "s") need attention.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* PWA install banner — rendered by JS only on mobile, hidden once dismissed or already installed *@
|
||||
@* PWA install banner — rendered by JS only on mobile, hidden once dismissed or already installed *@
|
||||
<div id="pwa-install-banner" class="row mb-4" style="display:none!important;">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-permanent mb-0 d-flex align-items-start gap-3 py-3"
|
||||
@@ -104,7 +104,7 @@
|
||||
@await Html.PartialAsync("_ShopProgressWidget", shopProgressWidget)
|
||||
}
|
||||
|
||||
@* Config health alert — only shown when there are setup gaps *@
|
||||
@* Config health alert — only shown when there are setup gaps *@
|
||||
@if (configHealth != null && !configHealth.IsHealthy)
|
||||
{
|
||||
<div class="row mb-4">
|
||||
@@ -406,15 +406,15 @@
|
||||
}
|
||||
@if (Model.AgingDays1To30 > 0)
|
||||
{
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-warn);"></span>1–30d @Model.AgingDays1To30.ToString("C0")</span>
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-warn);"></span>1–30d @Model.AgingDays1To30.ToString("C0")</span>
|
||||
}
|
||||
@if (Model.AgingDays31To60 > 0)
|
||||
{
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-bad);"></span>31–60d @Model.AgingDays31To60.ToString("C0")</span>
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-bad);"></span>31–60d @Model.AgingDays31To60.ToString("C0")</span>
|
||||
}
|
||||
@if (Model.AgingDays61To90 > 0)
|
||||
{
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-bad);"></span>61–90d @Model.AgingDays61To90.ToString("C0")</span>
|
||||
<span><span class="me-1" style="display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--pcl-bad);"></span>61–90d @Model.AgingDays61To90.ToString("C0")</span>
|
||||
}
|
||||
@if (Model.AgingDaysOver90 > 0)
|
||||
{
|
||||
@@ -536,7 +536,7 @@
|
||||
@if (line.EstCost.HasValue)
|
||||
{<span>@line.EstCost.Value.ToString("C")</span>}
|
||||
else
|
||||
{<span class="text-muted">—</span>}
|
||||
{<span class="text-muted">—</span>}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-outline-danger mark-ordered-btn text-nowrap"
|
||||
@@ -552,7 +552,7 @@
|
||||
<tr>
|
||||
<td colspan="2">Vendor Total</td>
|
||||
<td class="text-end">@vendorGroup.TotalLbsNeeded.ToString("N2") lbs</td>
|
||||
<td class="text-end">@(vendorGroup.TotalEstCost > 0 ? vendorGroup.TotalEstCost.ToString("C") : "—")</td>
|
||||
<td class="text-end">@(vendorGroup.TotalEstCost > 0 ? vendorGroup.TotalEstCost.ToString("C") : "—")</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@@ -571,7 +571,7 @@
|
||||
<div class="card border-0 shadow-sm dashboard-card" style="border-left: 4px solid #0d6efd !important;">
|
||||
<div class="card-header bg-body border-0 d-flex justify-content-between align-items-center pt-4 pb-3">
|
||||
<h5 class="mb-0 fw-bold">
|
||||
<i class="bi bi-box-arrow-in-down me-2 text-muted"></i>Powder Ordered — Awaiting Receipt
|
||||
<i class="bi bi-box-arrow-in-down me-2 text-muted"></i>Powder Ordered — Awaiting Receipt
|
||||
<span class="ms-2 text-muted fw-normal small" id="placed-count-label">@Model.PowderOrdersPlacedCount item@(Model.PowderOrdersPlacedCount == 1 ? "" : "s")</span>
|
||||
</h5>
|
||||
<small class="text-muted">Grouped by vendor · Enter lbs received to update inventory</small>
|
||||
@@ -630,13 +630,13 @@
|
||||
@if (line.EstCost.HasValue)
|
||||
{<span>@line.EstCost.Value.ToString("C")</span>}
|
||||
else
|
||||
{<span class="text-muted">—</span>}
|
||||
{<span class="text-muted">—</span>}
|
||||
</td>
|
||||
<td class="text-muted small">
|
||||
@if (line.OrderedAt.HasValue)
|
||||
{<span title="@line.OrderedAt.Value.Tz(ViewBag.CompanyTimeZone as string).ToString("g")">@line.OrderedAt.Value.Tz(ViewBag.CompanyTimeZone as string).ToString("MMM d")</span>}
|
||||
else
|
||||
{<span>—</span>}
|
||||
{<span>—</span>}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex align-items-center gap-1 justify-content-center receive-form-@line.CoatId">
|
||||
@@ -669,7 +669,7 @@
|
||||
<tr>
|
||||
<td colspan="2">Vendor Total</td>
|
||||
<td class="text-end">@vendorGroup.TotalLbsNeeded.ToString("N2") lbs</td>
|
||||
<td class="text-end">@(vendorGroup.TotalEstCost > 0 ? vendorGroup.TotalEstCost.ToString("C") : "—")</td>
|
||||
<td class="text-end">@(vendorGroup.TotalEstCost > 0 ? vendorGroup.TotalEstCost.ToString("C") : "—")</td>
|
||||
<td colspan="2"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
@@ -739,7 +739,7 @@
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Category</label>
|
||||
<select class="form-select" id="apm-categoryId" name="inventoryCategoryId">
|
||||
<option value="">— Select category —</option>
|
||||
<option value="">— Select category —</option>
|
||||
@if (ViewBag.InventoryCategories != null)
|
||||
{
|
||||
foreach (var cat in (IEnumerable<dynamic>)ViewBag.InventoryCategories)
|
||||
@@ -753,7 +753,7 @@
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Primary Vendor</label>
|
||||
<select class="form-select" id="apm-vendorId" name="primaryVendorId">
|
||||
<option value="">— Select vendor —</option>
|
||||
<option value="">— Select vendor —</option>
|
||||
@if (ViewBag.VendorList != null)
|
||||
{
|
||||
foreach (var v in (IEnumerable<dynamic>)ViewBag.VendorList)
|
||||
@@ -814,7 +814,7 @@
|
||||
</div>
|
||||
</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="submit" class="btn btn-primary" id="apm-saveBtn">
|
||||
<i class="bi bi-plus-circle me-1"></i>Add to Inventory
|
||||
</button>
|
||||
@@ -883,7 +883,7 @@
|
||||
|
||||
const esc = s => s ? s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"') : '';
|
||||
const estCost = (c.lbsToOrder && c.costPerLb) ? (c.lbsToOrder * c.costPerLb) : null;
|
||||
const orderedDate = c.orderedAt ? new Date(c.orderedAt).toLocaleDateString('en-US',{month:'short',day:'numeric'}) : '—';
|
||||
const orderedDate = c.orderedAt ? new Date(c.orderedAt).toLocaleDateString('en-US',{month:'short',day:'numeric'}) : '—';
|
||||
const lbsFmt = c.lbsToOrder ? parseFloat(c.lbsToOrder).toFixed(2) : '0.00';
|
||||
|
||||
// Find or create vendor group
|
||||
@@ -928,7 +928,7 @@
|
||||
${c.finish ? `<span class="badge bg-light text-dark border ms-1">${esc(c.finish)}</span>` : ''}
|
||||
</td>
|
||||
<td class="text-end fw-medium">${lbsFmt} lbs</td>
|
||||
<td class="text-end">${estCost ? '$' + estCost.toFixed(2) : '<span class="text-muted">—</span>'}</td>
|
||||
<td class="text-end">${estCost ? '$' + estCost.toFixed(2) : '<span class="text-muted">—</span>'}</td>
|
||||
<td class="text-muted small">${orderedDate}</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex align-items-center gap-1 justify-content-center receive-form-${c.coatId}">
|
||||
@@ -979,7 +979,7 @@
|
||||
}
|
||||
qtyInput.classList.remove('is-invalid');
|
||||
|
||||
// Custom powder (no inventory item) → open modal to add to inventory
|
||||
// Custom powder (no inventory item) → open modal to add to inventory
|
||||
if (!hasInv) {
|
||||
const modal = document.getElementById('addPowderModal');
|
||||
// Pre-fill hidden + text fields
|
||||
@@ -1024,7 +1024,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Inventory item exists → receive directly
|
||||
// Inventory item exists → receive directly
|
||||
const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value
|
||||
?? document.querySelector('meta[name="__RequestVerificationToken"]')?.content;
|
||||
|
||||
@@ -1065,7 +1065,7 @@
|
||||
?? document.querySelector('meta[name="__RequestVerificationToken"]')?.content;
|
||||
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Saving…';
|
||||
saveBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Saving…';
|
||||
|
||||
try {
|
||||
const resp = await fetch('@Url.Action("AddCustomPowderToInventory", "Dashboard")', {
|
||||
@@ -1094,7 +1094,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ââ€â‚¬Ã¢â€â‚¬ AI Lookup for Add Powder modal ââ€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬Ã¢â€â‚¬
|
||||
// ── AI Lookup for Add Powder modal ───────────────────────────────────────
|
||||
(function () {
|
||||
const apmBtn = document.getElementById('apm-ai-btn');
|
||||
const apmStatusEl = document.getElementById('apm-ai-status');
|
||||
@@ -1144,7 +1144,7 @@
|
||||
|
||||
const hasInput = manufacturer || colorName || colorCode || partNumber || itemName;
|
||||
if (!hasInput) {
|
||||
apmShowStatus('warning', '<i class="bi bi-exclamation-triangle me-1"></i>Fill in at least one field — Manufacturer, Color Name, Color Code, or Item Name — then try again.');
|
||||
apmShowStatus('warning', '<i class="bi bi-exclamation-triangle me-1"></i>Fill in at least one field — Manufacturer, Color Name, Color Code, or Item Name — then try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1153,7 +1153,7 @@
|
||||
document.getElementById('apm-bad-match-btn')?.remove();
|
||||
apmBtn.disabled = true;
|
||||
apmBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Looking up...';
|
||||
apmShowStatus('info', '<i class="bi bi-hourglass-split me-1"></i>Searching for product specifications…');
|
||||
apmShowStatus('info', '<i class="bi bi-hourglass-split me-1"></i>Searching for product specifications…');
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
@@ -1220,7 +1220,7 @@
|
||||
: '';
|
||||
apmShowStatus('success', `<i class="bi bi-check-circle me-1"></i>Auto-filled: ${filled.join(', ')}.${reasoning}`);
|
||||
} else {
|
||||
apmShowStatus('warning', '<i class="bi bi-info-circle me-1"></i>No new fields to fill — they may already be populated, or the product wasn\'t found.');
|
||||
apmShowStatus('warning', '<i class="bi bi-info-circle me-1"></i>No new fields to fill — they may already be populated, or the product wasn\'t found.');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
@@ -1274,7 +1274,7 @@
|
||||
(function () {
|
||||
var DISMISSED_KEY = 'pcl_pwa_banner_dismissed';
|
||||
|
||||
// Already installed as standalone — never show
|
||||
// Already installed as standalone — never show
|
||||
var isStandalone = window.navigator.standalone === true ||
|
||||
window.matchMedia('(display-mode: standalone)').matches;
|
||||
if (isStandalone) return;
|
||||
@@ -1298,7 +1298,7 @@
|
||||
var isSafari = /webkit/i.test(ua) && !/crios|chrome|fxios|opios/i.test(ua);
|
||||
if (isSafari) {
|
||||
titleEl.textContent = 'Add to Home Screen';
|
||||
msgEl.innerHTML = 'For the best experience — and so the camera only asks once — open the ' +
|
||||
msgEl.innerHTML = 'For the best experience — and so the camera only asks once — open the ' +
|
||||
'<strong>Share menu</strong> <span style="font-size:1.1em">▲</span> at the bottom of Safari ' +
|
||||
'and tap <strong>Add to Home Screen</strong>.';
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user