add progress steps to DOT compliance checker loading state

Shows animated step-by-step progress instead of generic spinner:
- Looking up FMCSA registration...
- Checking safety record...
- Verifying insurance filings...
- Reviewing operating authority...
- Checking state requirements...
- Verifying business entity status...
Each step gets a green checkmark as it 'completes'
This commit is contained in:
justin 2026-05-30 20:19:20 -05:00
parent d51a2f61b4
commit 77c3e8e3e3

View file

@ -19,8 +19,16 @@ Sign out
<!-- Name search --> <div id="tab-name" class="hidden"> <label for="name-input" class="block text-sm font-semibold text-gray-900 mb-2">Carrier Name</label> <div class="flex gap-3"> <input type="text" id="name-input" placeholder="Enter carrier name (e.g. Werner Enterprises)" class="flex-1 border border-gray-300 rounded-lg px-4 py-2.5 text-sm text-gray-900 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-orange-400 focus:border-orange-400"> <button type="button" id="name-btn" style="padding:10px 20px;background:#f3f4f6;color:#374151;font-weight:600;border-radius:8px;border:1px solid #d1d5db;cursor:pointer;white-space:nowrap;font-size:14px">Search</button> </div> <div id="name-results" class="hidden mt-4 max-h-60 overflow-y-auto space-y-2"></div> </div> </div>
<!-- Loading state --> <div id="pw-results-anchor"></div><div id="loading" class="hidden text-center py-12"> <div class="inline-flex items-center gap-3 text-gray-600"> <svg class="animate-spin h-5 w-5" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
Checking DOT compliance status...
<!-- Loading state with progress steps --> <div id="pw-results-anchor"></div><div id="loading" class="hidden py-8"> <div style="max-width:400px;margin:0 auto;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px"> <svg class="animate-spin" style="width:20px;height:20px;color:#f97316" viewBox="0 0 24 24"><circle style="opacity:0.25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle><path style="opacity:0.75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> <span style="font-size:15px;font-weight:600;color:#1a2744" id="loading-title">Running Compliance Check...</span></div>
<div id="loading-steps" style="font-size:13px;color:#475569;line-height:2.2">
<div id="step-fmcsa" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Looking up FMCSA registration...</div>
<div id="step-safety" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Checking safety record...</div>
<div id="step-insurance" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Verifying insurance filings...</div>
<div id="step-authority" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Reviewing operating authority...</div>
<div id="step-state" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Checking state requirements...</div>
<div id="step-entity" style="display:flex;align-items:center;gap:8px"><span style="color:#94a3b8">&#9679;</span> Verifying business entity status...</div>
</div>
</div> </div> <!-- Error state --> <div id="error-box" class="hidden bg-red-50 border border-red-200 rounded-xl p-6 mb-8"> <p class="text-sm text-red-800" id="error-msg"></p> </div> <!-- Results --> <div id="results" class="hidden space-y-4"></div> </div> </section> </main> <section class="py-10 border-t border-gray-100"> <div class="max-w-2xl mx-auto px-4 text-center"> <h2 class="text-lg font-bold text-gray-900 mb-2">Stay ahead of compliance changes</h2> <p class="text-sm text-gray-600 mb-5">Regulatory updates, enforcement trends, and compliance tips. No spam.</p> <button type="button" id="subscribe-btn-footer" class="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-pw-700 text-white text-sm font-medium hover:bg-pw-800 transition-colors"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75"></path></svg>
Join Mailing List
</button> </div> </section> <!-- Subscribe modal --> <div id="subscribe-modal-global" class="fixed inset-0 z-[9999] hidden items-center justify-center bg-black/50 backdrop-blur-sm"> <div class="bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 p-6"> <div class="flex justify-between items-start mb-4"> <h3 class="text-lg font-semibold text-gray-900">Join our mailing list</h3> <button type="button" id="subscribe-close-global" class="text-gray-400 hover:text-gray-600"> <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path></svg> </button> </div> <form id="subscribe-form-global" class="space-y-4"> <div> <label for="sub-name-g" class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400">(optional)</span></label> <input type="text" id="sub-name-g" placeholder="Your name" maxlength="100" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none"> </div> <div> <label for="sub-company-g" class="block text-sm font-medium text-gray-700 mb-1">Company <span class="text-gray-400">(optional)</span></label> <input type="text" id="sub-company-g" placeholder="Your company" maxlength="200" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none"> </div> <div> <label for="sub-email-g" class="block text-sm font-medium text-gray-700 mb-1">Email <span class="text-red-400">*</span></label> <input type="email" id="sub-email-g" required placeholder="you@company.com" maxlength="200" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none"> </div> <div class="flex items-start gap-2"> <input type="checkbox" id="sub-consent-g" required class="mt-1 rounded border-gray-300 text-pw-600 focus:ring-pw-500"> <label for="sub-consent-g" class="text-xs text-gray-600 leading-relaxed">
@ -105,18 +113,68 @@ Send reset link
dotBtn.addEventListener("click", runDotCheck);
dotInput.addEventListener("keydown", function(e) { if (e.key === "Enter") runDotCheck(); });
function markStep(stepId, status) {
var el = document.getElementById(stepId);
if (!el) return;
var dot = el.querySelector("span");
if (status === "done") {
dot.style.color = "#059669";
dot.innerHTML = "&#10004;";
el.style.color = "#059669";
} else if (status === "active") {
dot.style.color = "#f97316";
dot.innerHTML = "&#9679;";
el.style.color = "#1a2744";
el.style.fontWeight = "600";
} else if (status === "pending") {
dot.style.color = "#94a3b8";
dot.innerHTML = "&#9679;";
el.style.color = "#94a3b8";
el.style.fontWeight = "400";
}
}
function resetSteps() {
["step-fmcsa","step-safety","step-insurance","step-authority","step-state","step-entity"].forEach(function(id) { markStep(id, "pending"); });
}
async function animateSteps() {
var steps = [
{ id: "step-fmcsa", delay: 0 },
{ id: "step-safety", delay: 1200 },
{ id: "step-insurance", delay: 2400 },
{ id: "step-authority", delay: 3600 },
{ id: "step-state", delay: 5000 },
{ id: "step-entity", delay: 7000 },
];
for (var i = 0; i < steps.length; i++) {
await new Promise(function(r) { setTimeout(r, i === 0 ? 300 : steps[i].delay - steps[i-1].delay); });
if (i > 0) markStep(steps[i-1].id, "done");
markStep(steps[i].id, "active");
}
}
async function runDotCheck() {
var raw = dotInput.value.trim().replace(/\D/g, "");
if (!raw) { showError("Please enter a DOT number."); return; }
resetSteps();
loadingEl.classList.remove("hidden");
resultsEl.classList.add("hidden");
errorBox.classList.add("hidden");
// Animate steps while the API call runs
var animPromise = animateSteps();
try {
var res = await fetch(API + "/api/v1/dot/lookup?dot=" + raw);
var data = await res.json();
if (!res.ok) throw new Error(data.error || "Lookup failed");
// Mark all steps done
["step-fmcsa","step-safety","step-insurance","step-authority","step-state","step-entity"].forEach(function(id) { markStep(id, "done"); });
var title = document.getElementById("loading-title");
if (title) title.textContent = "Complete!";
await new Promise(function(r) { setTimeout(r, 500); });
renderResults(data);
} catch (e) {
showError(e.message || "Something went wrong. Try again.");