import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core"; import type { Transaction, Updateable } from "../generated/kysely-tailordb"; import { InvalidEmailError, InvalidNameError, InvalidStatusTransitionError, MissingRequiredFieldError, UserAlreadyExistsError, UserNotFoundError, } from "../lib/errors.generated"; export type UpdateOwnProfileInput = { name?: string; email?: string; }; const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export async function run>( db: Transaction, input: UpdateOwnProfileInput & Omit, "status">, ctx: CommandContext, ) { const { name, email, ...customFields } = input; const user = await db .selectFrom("User") .selectAll() .where("id", "=", ctx.actorId) .forUpdate() .executeTakeFirst(); if (!user) { return err(new UserNotFoundError(ctx.actorId)); } if (user.status !== "ACTIVE") { return err(new InvalidStatusTransitionError(`${user.status} to UPDATE_OWN_PROFILE`)); } if (name === undefined && email === undefined) { return err(new MissingRequiredFieldError("name or email")); } if (name?.trim() === "") { return err(new InvalidNameError(name)); } if (email !== undefined) { if (!EMAIL_PATTERN.test(email)) { return err(new InvalidEmailError(email)); } // Skip uniqueness check if email is unchanged if (email !== user.email) { const existingUser = await db .selectFrom("User") .selectAll() .where("email", "=", email) .forUpdate() .executeTakeFirst(); if (existingUser) { return err(new UserAlreadyExistsError(email)); } } } const updates: Updateable<"User"> = { ...(customFields as Updateable<"User">), }; if (name !== undefined) updates.name = name; if (email !== undefined) updates.email = email; const updatedUser = await db .updateTable("User") .set(updates) .where("id", "=", ctx.actorId) .returningAll() .executeTakeFirstOrThrow(); return ok({ user: updatedUser }); }