integratedhub/services/onboardingService.ts
2026-01-20 09:02:26 -06:00

155 lines
3.7 KiB
TypeScript

// services/onboardingService.ts
// Manages user onboarding state for landlord and tenant roles
import { kvStore } from "../modules/KvCacheStore.ts";
import type { User } from "../db/interfaces.ts";
import { getE2EUserById, isE2EEnabled, setE2EUser } from "./e2eFixtures.ts";
export type OnboardingRole = "landlord" | "tenant";
export interface OnboardingStatus {
user_id: string;
user_type: string | null;
onboarding_step: number | null;
onboarded_landlord: boolean | null;
onboarded_tenant: boolean | null;
email_verified: boolean | null;
banking_setup_complete: boolean | null;
}
async function getUser(userId: string): Promise<User | null> {
if (isE2EEnabled()) {
return getE2EUserById(userId);
}
return await kvStore.get<User>(["users", userId]);
}
async function setUser(user: User): Promise<void> {
if (isE2EEnabled()) {
setE2EUser(user);
return;
}
await kvStore.set(["users", user.id], user);
}
export async function startGuestOnboarding(
userId: string,
role: OnboardingRole,
): Promise<{ nextUrl: string }> {
const existing = await getUser(userId);
const now = Date.now();
if (!existing) {
throw new Error("User not found for onboarding");
}
const onboardingStep = role === "landlord"
? (existing.onboarding_step && existing.onboarding_step > 0
? existing.onboarding_step
: 1)
: (existing.onboarding_step ?? 0);
const updated: User = {
...existing,
user_type: role,
onboarding_step: onboardingStep,
onboarded_landlord: existing.onboarded_landlord ?? false,
onboarded_tenant: existing.onboarded_tenant ?? false,
email_verified: existing.email_verified ?? false,
banking_setup_complete: existing.banking_setup_complete ?? false,
updated_on: now,
};
await setUser(updated);
if (role === "landlord") {
return { nextUrl: "/landlord/onboarding/profile" };
}
// tenant
return { nextUrl: "/tenant/dashboard" };
}
export async function getOnboardingStatus(
userId: string,
): Promise<OnboardingStatus | null> {
const user = await getUser(userId);
if (!user) return null;
return {
user_id: user.id,
user_type: user.user_type || "",
onboarding_step: user.onboarding_step || 0,
onboarded_landlord: user.onboarded_landlord || false,
onboarded_tenant: user.onboarded_tenant || false,
email_verified: user.email_verified || false,
banking_setup_complete: user.banking_setup_complete || false,
};
}
export async function markRoleOnboarded(
userId: string,
role: OnboardingRole,
): Promise<void> {
const user = await getUser(userId);
if (!user) return;
const updated: User = {
...user,
onboarded_landlord: role === "landlord"
? true
: (user.onboarded_landlord ?? false),
onboarded_tenant: role === "tenant"
? true
: (user.onboarded_tenant ?? false),
onboarding_step: role === "landlord" ? 0 : (user.onboarding_step ?? 0),
updated_on: Date.now(),
};
await setUser(updated);
}
export async function setOnboardingStep(
userId: string,
step: number,
): Promise<void> {
const user = await getUser(userId);
if (!user) return;
const updated: User = {
...user,
onboarding_step: step,
updated_on: Date.now(),
};
await setUser(updated);
}
export async function markEmailVerified(userId: string): Promise<void> {
const user = await getUser(userId);
if (!user) return;
const updated: User = {
...user,
email_verified: true,
updated_on: Date.now(),
};
await setUser(updated);
}
export async function markBankingSetupComplete(
userId: string,
): Promise<void> {
const user = await getUser(userId);
if (!user) return;
const updated: User = {
...user,
banking_setup_complete: true,
updated_on: Date.now(),
};
await setUser(updated);
}