147 lines
3.6 KiB
TypeScript
147 lines
3.6 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";
|
|
|
|
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> {
|
|
return await kvStore.get<User>(["users", userId]);
|
|
}
|
|
|
|
async function setUser(user: User): Promise<void> {
|
|
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: send to tenant onboarding to collect basic info first
|
|
return { nextUrl: "/tenant/onboarding" };
|
|
}
|
|
|
|
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);
|
|
}
|