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:
@@ -1,12 +1,15 @@
|
||||
@model PowderCoating.Application.DTOs.Dashboard.DashboardViewModel
|
||||
@using Microsoft.AspNetCore.Html
|
||||
@using PowderCoating.Application.DTOs.Health
|
||||
@using PowderCoating.Web.ViewModels.Dashboard
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
var today = DateTime.Today;
|
||||
var currentMonth = DateTime.Now.ToString("MMMM yyyy");
|
||||
var configHealth = ViewBag.ConfigHealth as CompanyConfigHealth;
|
||||
var guidedActivationBanner = ViewBag.GuidedActivationBanner as PowderCoating.Web.ViewModels.GuidedActivation.GuidedActivationBannerViewModel;
|
||||
var shopProgressWidget = ViewBag.ShopProgressWidget as ShopProgressWidgetViewModel;
|
||||
}
|
||||
|
||||
<!-- Hero Brief -->
|
||||
@@ -56,6 +59,34 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (guidedActivationBanner?.Show == true)
|
||||
{
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm @(guidedActivationBanner.IsDismissed ? "" : "border-start border-4 border-primary")">
|
||||
<div class="card-body py-3">
|
||||
<div class="d-flex flex-column flex-lg-row gap-3 align-items-lg-center justify-content-between">
|
||||
<div>
|
||||
<div class="fw-semibold mb-1">@guidedActivationBanner.Title</div>
|
||||
<div class="text-muted">@guidedActivationBanner.Message</div>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-controller="GuidedActivation" asp-action="Start" class="btn @(guidedActivationBanner.IsDismissed ? "btn-outline-primary" : "btn-primary")">
|
||||
@guidedActivationBanner.ActionText
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (shopProgressWidget != null)
|
||||
{
|
||||
@await Html.PartialAsync("_ShopProgressWidget", shopProgressWidget)
|
||||
}
|
||||
|
||||
@* Config health alert — only shown when there are setup gaps *@
|
||||
@if (configHealth != null && !configHealth.IsHealthy)
|
||||
{
|
||||
@@ -777,6 +808,7 @@
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="~/js/shop-progress-widget.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
// Powder Orders - Mark as Ordered
|
||||
document.querySelectorAll('.mark-ordered-btn').forEach(btn => {
|
||||
@@ -1250,4 +1282,3 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
@using PowderCoating.Web.ViewModels.Dashboard
|
||||
@model ShopProgressWidgetViewModel
|
||||
|
||||
<div class="card border-0 shadow-sm mb-4" id="shopProgressWidget">
|
||||
<div class="card-header d-flex align-items-center gap-2 py-2 px-4"
|
||||
style="background:var(--pcl-paper-2);border-bottom:1px solid var(--pcl-rule);">
|
||||
<i class="bi bi-rocket-takeoff" style="color:var(--pcl-blue);"></i>
|
||||
<span class="fw-semibold" style="color:var(--pcl-ink);">Get the most out of your shop</span>
|
||||
@if (!Model.AllDone)
|
||||
{
|
||||
<span class="ms-auto badge rounded-pill bg-secondary">@Model.BadgeText</span>
|
||||
}
|
||||
<button class="btn btn-link btn-sm p-0 @(Model.AllDone ? "ms-auto" : "ms-2") text-secondary"
|
||||
id="shopProgressToggle" title="Collapse" style="line-height:1;">
|
||||
<i class="bi bi-chevron-up" id="shopProgressChevron"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="shopProgressBody">
|
||||
@if (Model.AllDone)
|
||||
{
|
||||
<div class="px-4 py-4 text-center">
|
||||
<div class="fw-semibold mb-1" style="font-size:1rem;color:var(--pcl-ink);">Your shop is fully set up 🎉</div>
|
||||
<div class="text-muted mb-3" style="font-size:0.85rem;">You're ready to run everything from here.</div>
|
||||
<a href="@Url.Action("Create", "Jobs")" class="btn btn-primary btn-sm">
|
||||
Create job <i class="bi bi-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="px-4 pt-3 pb-1">
|
||||
<p class="text-muted mb-2" style="font-size:0.85rem;">@Model.SubtitleText</p>
|
||||
<div class="progress mb-1" style="height:5px;border-radius:3px;">
|
||||
<div class="progress-bar @(Model.ProgressPercent >= 60 ? "bg-success" : "bg-primary")"
|
||||
role="progressbar"
|
||||
style="width:@Model.ProgressPercent%;transition:width 0.4s ease;"
|
||||
aria-valuenow="@Model.ProgressPercent" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list-group list-group-flush mb-1">
|
||||
@{
|
||||
var nextFound = false;
|
||||
bool? prevDone = null;
|
||||
}
|
||||
@foreach (var item in Model.Items)
|
||||
{
|
||||
var isNext = !item.Done && !nextFound;
|
||||
if (isNext) { nextFound = true; }
|
||||
|
||||
@if (prevDone.HasValue && prevDone.Value && !item.Done)
|
||||
{
|
||||
<li class="list-group-item border-0 px-4 py-0" style="background:transparent;">
|
||||
<hr class="my-0" style="border-color:var(--pcl-rule);">
|
||||
</li>
|
||||
}
|
||||
prevDone = item.Done;
|
||||
|
||||
<li class="list-group-item border-0 d-flex align-items-center gap-3 px-4 py-2"
|
||||
style="background:@(isNext ? "rgba(13,110,253,0.04)" : "transparent");">
|
||||
@if (item.Done)
|
||||
{
|
||||
<i class="bi bi-check-circle-fill flex-shrink-0"
|
||||
style="color:var(--pcl-good);font-size:1.1rem;"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="bi @item.Icon flex-shrink-0 text-muted" style="font-size:1.1rem;"></i>
|
||||
}
|
||||
|
||||
<div class="flex-grow-1 min-width-0">
|
||||
<div class="fw-medium @(item.Done ? "text-muted" : "")" style="font-size:0.875rem;">
|
||||
@item.Label
|
||||
@if (isNext)
|
||||
{
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary rounded-pill ms-1" style="font-size:0.65rem;">Next</span>
|
||||
}
|
||||
</div>
|
||||
@if (item.Done)
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(item.DoneSubLabel))
|
||||
{
|
||||
<div style="font-size:0.78rem;color:var(--pcl-good);">@item.DoneSubLabel</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-muted" style="font-size:0.78rem;">@item.SubLabel</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (item.Done)
|
||||
{
|
||||
<span class="flex-shrink-0 fw-medium"
|
||||
style="font-size:0.78rem;color:var(--pcl-good);">Done</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@item.CtaUrl"
|
||||
class="btn btn-sm @(isNext ? "btn-primary" : "btn-outline-primary") flex-shrink-0">
|
||||
@item.CtaText <i class="bi bi-arrow-right ms-1"></i>
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user