fix(FN-2402): preserve onboarding stepper done state on completion
- Treat the completion view as an effective final step so all prior indicators/connectors remain marked done - Mark the first-task step as completed before tracking onboarding completion and clear it from skipped state - Replace hardcoded success colors in setup wizard stepper done styles with theme-aware done status tokens - Add regression assertions verifying all step indicators and connectors stay in done state on the completion screen
This commit is contained in:
@@ -614,8 +614,10 @@ export function ModelOnboardingModal({
|
||||
{ key: "first-task" as const, label: "First Task" },
|
||||
];
|
||||
|
||||
// Get current step index for progress indicator
|
||||
// Get current step index for progress indicator.
|
||||
// Keep the stepper in a fully-complete visual state when the completion screen is shown.
|
||||
const currentStepIndex = steps.findIndex((s) => s.key === step);
|
||||
const effectiveStepIndex = step === "complete" ? steps.length : currentStepIndex;
|
||||
|
||||
// Persist step state whenever it changes (for resume functionality)
|
||||
useEffect(() => {
|
||||
@@ -1327,10 +1329,14 @@ export function ModelOnboardingModal({
|
||||
|
||||
// Complete onboarding
|
||||
const handleComplete = useCallback(async () => {
|
||||
const nextCompletedSteps = [...new Set([...completedSteps, "first-task"])] as OnboardingStep[];
|
||||
|
||||
setSaving(true);
|
||||
try {
|
||||
await completeOnboarding();
|
||||
trackOnboardingEvent("onboarding:completed", { completedSteps, skippedSteps });
|
||||
trackOnboardingEvent("onboarding:completed", { completedSteps: nextCompletedSteps, skippedSteps });
|
||||
setCompletedSteps(nextCompletedSteps);
|
||||
setSkippedSteps((prev) => prev.filter((s) => s !== "first-task"));
|
||||
setStep("complete");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
@@ -1757,9 +1763,10 @@ export function ModelOnboardingModal({
|
||||
{/* Step indicator - 3 progress steps + complete */}
|
||||
<div className="model-onboarding-steps">
|
||||
{steps.map((s, index) => {
|
||||
// A step is done if it's in completedSteps AND is before current position
|
||||
const isDone = completedSteps.includes(s.key) && currentStepIndex > index;
|
||||
const isSkipped = skippedSteps.includes(s.key) && !completedSteps.includes(s.key) && currentStepIndex > index;
|
||||
// A step is done/skipped only once we have progressed beyond it.
|
||||
const hasProgressedPastStep = effectiveStepIndex > index;
|
||||
const isDone = completedSteps.includes(s.key) && hasProgressedPastStep;
|
||||
const isSkipped = skippedSteps.includes(s.key) && !completedSteps.includes(s.key) && hasProgressedPastStep;
|
||||
// Clickable if it's a completed/skipped step (can review)
|
||||
const isClickable = isDone || isSkipped;
|
||||
return (
|
||||
@@ -1767,7 +1774,7 @@ export function ModelOnboardingModal({
|
||||
{index > 0 && (
|
||||
<div
|
||||
className={`model-onboarding-step-connector ${
|
||||
index <= currentStepIndex ? "done" : ""
|
||||
index <= effectiveStepIndex ? "done" : ""
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -565,8 +565,9 @@
|
||||
}
|
||||
|
||||
.model-onboarding-step-indicator.done .step-number {
|
||||
background: var(--success, #22c55e);
|
||||
color: #fff;
|
||||
background: var(--status-done-bg-deep);
|
||||
border: 1px solid color-mix(in srgb, var(--done) 45%, transparent);
|
||||
color: var(--done);
|
||||
}
|
||||
|
||||
.model-onboarding-step-indicator.done {
|
||||
@@ -618,7 +619,7 @@
|
||||
}
|
||||
|
||||
.model-onboarding-step-connector.done {
|
||||
background: var(--success, #22c55e);
|
||||
background: var(--done);
|
||||
}
|
||||
|
||||
/* Content area */
|
||||
|
||||
@@ -2094,6 +2094,15 @@ describe("ModelOnboardingModal", () => {
|
||||
expect(screen.getByText("All Set!")).toBeTruthy();
|
||||
});
|
||||
|
||||
// Completed indicators should remain in done state on the completion screen.
|
||||
const aiSetupIndicator = screen.getByRole("button", { name: "Go back to AI Setup" });
|
||||
const githubIndicator = screen.getByRole("button", { name: "Go back to GitHub" });
|
||||
const firstTaskIndicator = screen.getByRole("button", { name: "Go back to First Task" });
|
||||
expect(aiSetupIndicator).toHaveClass("done");
|
||||
expect(githubIndicator).toHaveClass("done");
|
||||
expect(firstTaskIndicator).toHaveClass("done");
|
||||
expect(document.querySelectorAll(".model-onboarding-step-connector.done")).toHaveLength(2);
|
||||
|
||||
// Click Get Started to close
|
||||
fireEvent.click(screen.getByText("Get Started"));
|
||||
expect(onComplete).toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user