* Production readiness phase 1: journeys doc, onboarding persistence, teardown, chat UX - Add docs/PRODUCTION-READINESS.md with journey matrix and defect backlog - Persist onboarding draft in session storage across reload - Localize onboarding discovery main-bot role; German Chief of Staff string - Destroy team sandboxes and home folders when deleting an account - Keep composer/transcript visible during queued runs; extend send watchdog - Add Playwright specs: reload persistence, DE intake, queued progress, console guard Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * Fix CI: sync Lingui catalogs, onboarding name draft, E2E stability - Localize Chief of Staff in discovery shell; sync all locale PO files - Propagate Oo name to onboarding draft while typing - Adjust DE intake, queued-run, and console-error E2E specs Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * Format chat-queued-progress E2E spec Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * fix(api): scope account-delete sandbox teardown to owned spaces Only destroy team sandboxes and homes for spaces the deleting user owns with no remaining members or bots. Guard home removal under dataDir/homes. Add unit and postgres journey coverage. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> * chore: apply biome formatting Co-authored-by: cynarAI <cynarAI@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
import type { OnboardingEmojiConfig } from "../components/onboarding/OnboardingOoDesigner";
|
|
import type { UiLocale } from "./ui-locale";
|
|
|
|
export const ONBOARDING_DRAFT_STORAGE_KEY = "rakazo.onboardingDraft";
|
|
|
|
export type OnboardingStepDraft =
|
|
| "emoji"
|
|
| "name"
|
|
| "language"
|
|
| "voice"
|
|
| "integrations"
|
|
| "scan"
|
|
| "bot";
|
|
|
|
export type OnboardingDraft = {
|
|
step: OnboardingStepDraft;
|
|
emojiConfig: OnboardingEmojiConfig;
|
|
ooName: string;
|
|
languageChoice: UiLocale | null;
|
|
currentLocale: UiLocale;
|
|
selectedVoiceId: string;
|
|
voiceSkipped: boolean;
|
|
};
|
|
|
|
function readStorage(
|
|
storage: Pick<Storage, "getItem"> | null | undefined,
|
|
): Pick<Storage, "getItem"> | null {
|
|
if (storage !== undefined) return storage;
|
|
return typeof sessionStorage !== "undefined" ? sessionStorage : null;
|
|
}
|
|
|
|
function writeStorage(
|
|
storage: Pick<Storage, "setItem" | "removeItem"> | null | undefined,
|
|
): Pick<Storage, "setItem" | "removeItem"> | null {
|
|
if (storage !== undefined) return storage;
|
|
return typeof sessionStorage !== "undefined" ? sessionStorage : null;
|
|
}
|
|
|
|
/** Restore in-progress onboarding after reload or deep link within the same tab. */
|
|
export function readOnboardingDraft(
|
|
storage: Pick<Storage, "getItem"> | null = readStorage(undefined),
|
|
): OnboardingDraft | null {
|
|
if (!storage) return null;
|
|
try {
|
|
const raw = storage.getItem(ONBOARDING_DRAFT_STORAGE_KEY);
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(raw) as Partial<OnboardingDraft>;
|
|
if (
|
|
!parsed ||
|
|
typeof parsed.step !== "string" ||
|
|
!parsed.emojiConfig ||
|
|
typeof parsed.emojiConfig.avatarValue !== "string" ||
|
|
typeof parsed.emojiConfig.pose !== "string" ||
|
|
typeof parsed.ooName !== "string"
|
|
) {
|
|
return null;
|
|
}
|
|
return {
|
|
step: parsed.step as OnboardingStepDraft,
|
|
emojiConfig: parsed.emojiConfig,
|
|
ooName: parsed.ooName,
|
|
languageChoice: parsed.languageChoice ?? null,
|
|
currentLocale: parsed.currentLocale ?? "en",
|
|
selectedVoiceId: parsed.selectedVoiceId ?? "",
|
|
voiceSkipped: parsed.voiceSkipped ?? false,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function persistOnboardingDraft(
|
|
draft: OnboardingDraft,
|
|
storage: Pick<Storage, "setItem"> | null = writeStorage(undefined),
|
|
): void {
|
|
if (!storage) return;
|
|
try {
|
|
storage.setItem(ONBOARDING_DRAFT_STORAGE_KEY, JSON.stringify(draft));
|
|
} catch {
|
|
// Quota / private mode — onboarding still works without persistence.
|
|
}
|
|
}
|
|
|
|
export function clearOnboardingDraft(
|
|
storage: Pick<Storage, "removeItem"> | null = writeStorage(undefined),
|
|
): void {
|
|
if (!storage) return;
|
|
try {
|
|
storage.removeItem(ONBOARDING_DRAFT_STORAGE_KEY);
|
|
} catch {
|
|
// Ignore storage failures on completion.
|
|
}
|
|
}
|