Onboarding overhaul: slim wizard, progress widget, guided activation UX

Setup Wizard: reduced from 10 steps to 5 (Company Info → QB Migration →
Pricing Defaults → Named Ovens → Notifications). Removed Doc Numbering,
Job Settings, Payment Terms, Pricing Tiers, and Team Members steps — these
all have sensible defaults and are accessible any time in Company Settings.
Wizard now completes in ~5 minutes instead of 15–20.

Dashboard progress widget (new): "Get the most out of your shop" checklist
appears for Company Admins after wizard completion. Tracks six post-setup
activation tasks with dynamic progress badge, motivating subtitle copy,
collapsed-state persistence via localStorage, and a full completion state
("Your shop is fully set up 🎉") that replaces the checklist at 100%.
The next recommended step is highlighted with a solid CTA button and a
subtle blue row tint. Completed steps show encouraging green subtext instead
of just "Done". Widget disappears from controller when AllDone would have
caused a silent vanish — now renders the completion state instead.

Guided activation (Daily Board): rewrote the BoardIntroStep callout to lead
with "This is your shop in real time" and a plain-English description of the
board's purpose. Added a separate InstructionText field to
GuidedActivationCalloutViewModel so the "Move this job to the next stage"
action prompt renders as a distinct bold line with an arrow icon rather than
being buried in the body copy. After the stage change, the confirmation
callout now reads "Nice — your workflow just updated" to reinforce what just
happened before prompting the invoice step.

All copy passes the "shop owner, not SaaS" test: no technical jargon,
benefit-driven descriptions, natural language throughout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 21:10:47 -04:00
parent 4d27a378ac
commit 8aae30765f
30 changed files with 10870 additions and 333 deletions
@@ -19,8 +19,10 @@
<form asp-action="PostStep4" method="post" onsubmit="return validateStep4()">
@Html.AntiForgeryToken()
<input type="hidden" name="OvensJson" id="ovensJson" value="@Html.Raw(Model.OvensJson ?? "[]")" />
<input type="hidden" name="BlastSetupsJson" id="blastSetupsJson" value="@Html.Raw(Model.BlastSetupsJson ?? "[]")" />
<script type="application/json" id="ovensSeedJson">@Html.Raw(Model.OvensJson ?? "[]")</script>
<script type="application/json" id="blastSetupsSeedJson">@Html.Raw(Model.BlastSetupsJson ?? "[]")</script>
<input type="hidden" name="OvensJson" id="ovensJson" value="[]" />
<input type="hidden" name="BlastSetupsJson" id="blastSetupsJson" value="[]" />
<!-- ── Ovens ─────────────────────────────────────────────────────── -->
<div class="wizard-card">
@@ -75,7 +77,7 @@
// ═══════════════════════════════════════════════════════════════════════════
// OVENS
// ═══════════════════════════════════════════════════════════════════════════
var ovens = JSON.parse(document.getElementById('ovensJson').value || '[]');
var ovens = JSON.parse(document.getElementById('ovensSeedJson').textContent || '[]');
function serializeOvens() {
document.getElementById('ovensJson').value = JSON.stringify(
@@ -212,7 +214,7 @@
// ═══════════════════════════════════════════════════════════════════════════
// BLAST SETUPS
// ═══════════════════════════════════════════════════════════════════════════
var blasts = JSON.parse(document.getElementById('blastSetupsJson').value || '[]');
var blasts = JSON.parse(document.getElementById('blastSetupsSeedJson').textContent || '[]');
function serializeBlasts() {
document.getElementById('blastSetupsJson').value = JSON.stringify(