import { Controller, Get, Patch, Body, Req, UseInterceptors, ClassSerializerInterceptor, UseGuards, } from "@nestjs/common"; import { UsersFacade } from "./application/users.facade.js"; import { createZodDto, ZodResponse, ZodSerializerDto } from "nestjs-zod"; import { updateCustomerProfileRequestSchema } from "@customer-portal/domain/auth"; import { dashboardSummarySchema } from "@customer-portal/domain/dashboard"; import { addressSchema, userSchema } from "@customer-portal/domain/customer"; import { bilingualAddressSchema } from "@customer-portal/domain/address"; import type { Address } from "@customer-portal/domain/customer"; import type { User } from "@customer-portal/domain/customer"; import type { RequestWithUser } from "@bff/modules/auth/auth.types.js"; import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js"; class UpdateAddressDto extends createZodDto(addressSchema.partial()) {} class UpdateBilingualAddressDto extends createZodDto(bilingualAddressSchema) {} class UpdateCustomerProfileRequestDto extends createZodDto(updateCustomerProfileRequestSchema) {} class AddressDto extends createZodDto(addressSchema) {} class UserDto extends createZodDto(userSchema) {} class DashboardSummaryDto extends createZodDto(dashboardSummarySchema) {} @Controller("me") @UseInterceptors(ClassSerializerInterceptor) export class UsersController { constructor(private usersFacade: UsersFacade) {} /** * GET /me - Get complete customer profile (includes address) * Profile data fetched from WHMCS (single source of truth) */ @UseGuards(SalesforceReadThrottleGuard) @Get() @ZodResponse({ description: "Get user profile", type: UserDto }) async getProfile(@Req() req: RequestWithUser): Promise { // This endpoint represents the authenticated user; treat missing user as an error. return this.usersFacade.getProfile(req.user.id); } /** * GET /me/summary - Get dashboard summary */ @UseGuards(SalesforceReadThrottleGuard) @Get("summary") @ZodResponse({ description: "Get dashboard summary", type: DashboardSummaryDto }) async getSummary(@Req() req: RequestWithUser) { return this.usersFacade.getUserSummary(req.user.id); } /** * GET /me/address - Get customer address only */ @UseGuards(SalesforceReadThrottleGuard) @Get("address") @ZodSerializerDto(addressSchema.nullable()) async getAddress(@Req() req: RequestWithUser): Promise
{ return this.usersFacade.getAddress(req.user.id); } /** * PATCH /me/address - Update address fields */ @Patch("address") @ZodResponse({ description: "Update address", type: AddressDto }) async updateAddress( @Req() req: RequestWithUser, @Body() address: UpdateAddressDto ): Promise
{ return this.usersFacade.updateAddress(req.user.id, address); } /** * PATCH /me/address/bilingual - Update address with Japanese + English fields * * Dual-write: English to WHMCS (source of truth), Japanese to Salesforce * * Use this endpoint when you have both Japanese and English address data * (e.g., from Japan Post ZIP code lookup). * * @example * { * "postcode": "1000001", * "prefecture": "Tokyo", * "prefectureJa": "東京都", * "city": "Chiyoda-ku", * "cityJa": "千代田区", * "town": "Chiyoda", * "townJa": "千代田", * "buildingName": "Example Building", * "roomNumber": "101", * "residenceType": "apartment" * } */ @Patch("address/bilingual") @ZodResponse({ description: "Update bilingual address", type: AddressDto }) async updateBilingualAddress( @Req() req: RequestWithUser, @Body() address: UpdateBilingualAddressDto ): Promise
{ return this.usersFacade.updateBilingualAddress(req.user.id, address); } /** * PATCH /me - Update customer profile (can update profile fields and/or address) * All fields optional - only send what needs to be updated * Updates stored in WHMCS (single source of truth) * * Examples: * - Update name only: { firstname: "John", lastname: "Doe" } * - Update address only: { address1: "123 Main St", city: "Tokyo" } * - Update both: { firstname: "John", address1: "123 Main St" } */ @Patch() @ZodResponse({ description: "Update profile", type: UserDto }) async updateProfile( @Req() req: RequestWithUser, @Body() updateData: UpdateCustomerProfileRequestDto ) { return this.usersFacade.updateProfile(req.user.id, updateData); } }