Files
fusion/packages/dashboard/app/components/OnboardingResumeCard.tsx
gsxdsm 1e49494bac feat(i18n): full-sweep string migration — 5,930 keys across 5 locales (#1352)
Migration (multi-agent sweep over 216 files, 60 batches):
- Every user-visible dashboard + TUI string moved to t() with the exact
  English inline default (en rendering byte-identical)
- Catalogs merged from per-batch fragments: en/zh-CN/zh-TW/fr/es now
  carry ~5,930 keys each across common/app/errors/cli namespaces;
  CLI bundles regenerated (6 locales incl. ko)

Integration fixes:
- 18 type errors: reserved {{count}} interpolations renamed, malformed
  plural call, hand-rolled t-param types replaced with TFunction<"app">
- 23 lint errors: superseded label constants/helpers removed
- ExecutorStatusBar hook-order violation (keyboard-open early return
  moved below hooks)
- TUI tests wrapped in I18nextProvider (uninitialized fallback renders
  literal {{placeholders}}); dashboard vitest.setup boots a minimal en
  i18next instance for the same reason

Known WIP (next commits): ~457 residual strings across 50 batches,
Korean drafts for swept keys, and a dashboard test-suite pass that is
still being stabilized (~283 failures under investigation — fake-timer
waitFor interaction, likely stale node_modules vs merged lockfile).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 19:06:53 -07:00

67 lines
2.6 KiB
TypeScript

import "./OnboardingResumeCard.css";
import { Play, Sparkles } from "lucide-react";
import { useTranslation } from "react-i18next";
import { getOnboardingResumeStep, ONBOARDING_FLOW_STEPS } from "./model-onboarding-state";
import { trackOnboardingEvent } from "./onboarding-events";
interface OnboardingResumeCardProps {
/** Called when the user clicks "Continue onboarding" */
onResume: () => void;
}
/**
* A banner/card that appears when a user has previously started onboarding
* but dismissed the modal without completing. It allows them to resume
* from where they left off.
*/
export function OnboardingResumeCard({ onResume }: OnboardingResumeCardProps) {
const { t } = useTranslation("app");
const resumeStep = getOnboardingResumeStep();
// Should not render if no resumable state exists
if (!resumeStep) {
return null;
}
const completedCount = resumeStep.completedSteps.length;
const totalSteps = ONBOARDING_FLOW_STEPS.length;
const progressText = completedCount > 0
? t("onboarding.progressText", "{{completed}} of {{total}} step{{pluralS}} complete — You're on the ", { completed: completedCount, total: totalSteps, pluralS: completedCount !== 1 ? "s" : "" })
: t("onboarding.onTheStep", "You're on the ");
return (
<section
className="onboarding-resume-card"
role="region"
aria-label={t("onboarding.resumeOnboarding", "Resume onboarding")}
>
<div className="onboarding-resume-card__main">
<div className="onboarding-resume-card__icon" aria-hidden="true">
<Sparkles size={20} />
</div>
<div className="onboarding-resume-card__content">
<h2 className="onboarding-resume-card__title">{t("onboarding.continueSetup", "Continue Setup")}</h2>
<p className="onboarding-resume-card__description">
{progressText}<strong>{resumeStep.label}</strong> {t("onboarding.stepContinue", "step. Continue where you left off to complete your dashboard setup.")}
</p>
</div>
</div>
<div className="onboarding-resume-card__actions">
<button
className="onboarding-resume-card__resume-btn btn btn-primary btn-sm"
onClick={() => {
trackOnboardingEvent("onboarding:resumed", {
source: "resume-card",
resumedFromStep: resumeStep.currentStep,
});
onResume();
}}
>
<Play size={14} aria-hidden="true" />
<span>{t("onboarding.continueOnboarding", "Continue onboarding")}</span>
</button>
</div>
</section>
);
}