fix(onboarding): derive step counter from STEP_ORDER

All six step panels previously hard-coded "X / 5" or "X / 6" in their
header captions, which drifted out of sync with the 7-step STEP_ORDER in
OnboardingHero. Export STEP_ORDER / TOTAL_STEPS / getStepNumber from
OnboardingHero and consume them in every step so the counter is always
the single source of truth.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 22:03:57 -04:00
1 parent 5a5587c2a1
commit 183f22e922
7 files changed
+23 -9

No files matched your search

@@ -3,6 +3,7 @@ import { Buildings, FolderOpen } from "@phosphor-icons/react";
import type { ConnectorId, ProfileSnapshot } from "@shared/types"; import type { ConnectorId, ProfileSnapshot } from "@shared/types";
import type { AppLocale } from "@shared/locale"; import type { AppLocale } from "@shared/locale";
import { ActionButton, Input, TextArea, Select } from "../ui"; import { ActionButton, Input, TextArea, Select } from "../ui";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
interface CompanyStepProps { interface CompanyStepProps {
companyName: string; companyName: string;
@@ -57,7 +58,7 @@ export function CompanyStep({
className="bg-card border border-border shadow-sm w-full rounded-lg p-7" className="bg-card border border-border shadow-sm w-full rounded-lg p-7"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabelCompany")} 3 / 6 {t("onboarding.stepLabelCompany")} {getStepNumber("company")} / {TOTAL_STEPS}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.step1Title")} {t("onboarding.step1Title")}
@@ -1,6 +1,7 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { ArrowRight, Brain, UserCircle, type Icon } from "@phosphor-icons/react"; import { ArrowRight, Brain, UserCircle, type Icon } from "@phosphor-icons/react";
import type { ExecutionMode } from "@shared/types"; import type { ExecutionMode } from "@shared/types";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
interface ExecutionModeStepProps { interface ExecutionModeStepProps {
executionMode: ExecutionMode; executionMode: ExecutionMode;
@@ -27,7 +28,7 @@ export function ExecutionModeStep({
className="bg-card border border-border shadow-sm w-full rounded-lg p-7" className="bg-card border border-border shadow-sm w-full rounded-lg p-7"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabelMode")} 5 / 6 {t("onboarding.stepLabelMode")} {getStepNumber("executionmode")} / {TOTAL_STEPS}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.modeStepTitle")} {t("onboarding.modeStepTitle")}
@@ -1,6 +1,7 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Cpu, Lightning } from "@phosphor-icons/react"; import { Cpu, Lightning } from "@phosphor-icons/react";
import { ActionButton, Input, TextArea } from "../ui"; import { ActionButton, Input, TextArea } from "../ui";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
interface MissionStepProps { interface MissionStepProps {
goalTitle: string; goalTitle: string;
@@ -35,7 +36,7 @@ export function MissionStep({
className="bg-card border border-border shadow-sm w-full rounded-lg p-7" className="bg-card border border-border shadow-sm w-full rounded-lg p-7"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabelMission")} 6 / 6 {t("onboarding.stepLabelMission")} {getStepNumber("mission")} / {TOTAL_STEPS}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.step2Title")} {t("onboarding.step2Title")}
@@ -4,6 +4,7 @@ import type { ConnectorId, ProfileSnapshot } from "@shared/types";
import type { AppLocale } from "@shared/locale"; import type { AppLocale } from "@shared/locale";
import { ActionButton } from "../ui"; import { ActionButton } from "../ui";
import { ModelToggleList, StatusBadge } from "../execution/ModelManagementShared"; import { ModelToggleList, StatusBadge } from "../execution/ModelManagementShared";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
interface ModelPoolStepProps { interface ModelPoolStepProps {
currentLocale: AppLocale; currentLocale: AppLocale;
@@ -52,7 +53,7 @@ export function ModelPoolStep({
className="w-full rounded-lg border border-border bg-card p-7 shadow-sm" className="w-full rounded-lg border border-border bg-card p-7 shadow-sm"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabelModelPool")} 4 / 6 {t("onboarding.stepLabelModelPool")} {getStepNumber("modelpool")} / {TOTAL_STEPS}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.modelPoolStepTitle")} {t("onboarding.modelPoolStepTitle")}
@@ -8,6 +8,7 @@ import {
} from "@phosphor-icons/react"; } from "@phosphor-icons/react";
import { ActionButton } from "../ui"; import { ActionButton } from "../ui";
import type { TerminalCommandKey } from "@shared/contracts"; import type { TerminalCommandKey } from "@shared/contracts";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
interface NodeCheckStepProps { interface NodeCheckStepProps {
nodeStatus: { installed: boolean; version: string | null } | null; nodeStatus: { installed: boolean; version: string | null } | null;
@@ -69,7 +70,7 @@ export function NodeCheckStep({
className="bg-card border border-border shadow-sm w-full rounded-lg p-7" className="bg-card border border-border shadow-sm w-full rounded-lg p-7"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabel1")} 1 / 5 — {t("onboarding.nodeCheckStep")} {t("onboarding.stepLabel1")} {getStepNumber("nodecheck")} / {TOTAL_STEPS} — {t("onboarding.nodeCheckStep")}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.nodeCheckTitle")} {t("onboarding.nodeCheckTitle")}
@@ -31,7 +31,7 @@ const STEP_LABELS_CONFIG = [
{ key: "onboarding.stepLabelLaunch", icon: Rocket, step: "launch" as const }, { key: "onboarding.stepLabelLaunch", icon: Rocket, step: "launch" as const },
] as const; ] as const;
const STEP_ORDER: OnboardingStep[] = [ export const STEP_ORDER = [
"nodecheck", "nodecheck",
"providers", "providers",
"company", "company",
@@ -39,7 +39,15 @@ const STEP_ORDER: OnboardingStep[] = [
"executionmode", "executionmode",
"mission", "mission",
"launch", "launch",
]; ] as const satisfies readonly OnboardingStep[];
export type OnboardingStepName = (typeof STEP_ORDER)[number];
export const TOTAL_STEPS = STEP_ORDER.length;
export function getStepNumber(step: OnboardingStepName): number {
return STEP_ORDER.indexOf(step) + 1;
}
interface OnboardingHeroProps { interface OnboardingHeroProps {
step: OnboardingStep; step: OnboardingStep;
@@ -52,7 +60,7 @@ export function OnboardingHero({
currentLocale: _currentLocale, currentLocale: _currentLocale,
t, t,
}: OnboardingHeroProps) { }: OnboardingHeroProps) {
const stepIndex = STEP_ORDER.indexOf(step); const stepIndex = (STEP_ORDER as readonly OnboardingStep[]).indexOf(step);
const stepLabels = STEP_LABELS_CONFIG.map((cfg) => ({ const stepLabels = STEP_LABELS_CONFIG.map((cfg) => ({
label: t(cfg.key), label: t(cfg.key),
@@ -11,6 +11,7 @@ import {
StatusBadge, StatusBadge,
INPUT_CLS, INPUT_CLS,
} from "./ProvidersStepShared"; } from "./ProvidersStepShared";
import { getStepNumber, TOTAL_STEPS } from "./OnboardingHero";
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Props */ /* Props */
@@ -104,7 +105,7 @@ export function ProvidersStep({
className="w-full rounded-lg border border-border bg-card p-7 shadow-sm" className="w-full rounded-lg border border-border bg-card p-7 shadow-sm"
> >
<div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground"> <div className="mb-2 text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
{t("onboarding.stepLabelProviders")} 2 / 6 {t("onboarding.stepLabelProviders")} {getStepNumber("providers")} / {TOTAL_STEPS}
</div> </div>
<div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground"> <div className="mb-1 text-[18px] font-semibold tracking-[-0.01em] text-foreground">
{t("onboarding.providersTitle")} {t("onboarding.providersTitle")}