diff --git a/apps/bff/src/modules/subscriptions/sim-management.service.ts b/apps/bff/src/modules/subscriptions/sim-management.service.ts index 716a3cba..cb019fc3 100644 --- a/apps/bff/src/modules/subscriptions/sim-management.service.ts +++ b/apps/bff/src/modules/subscriptions/sim-management.service.ts @@ -1,7 +1,7 @@ import { Injectable } from "@nestjs/common"; import { SimOrchestratorService } from "./sim-management/services/sim-orchestrator.service"; import { SimNotificationService } from "./sim-management/services/sim-notification.service"; -import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; +import type { SimDetails, SimInfo, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; import type { SimTopUpRequest, SimPlanChangeRequest, @@ -123,10 +123,7 @@ export class SimManagementService { async getSimInfo( userId: string, subscriptionId: number - ): Promise<{ - details: SimDetails; - usage: SimUsage; - }> { + ): Promise { return this.simOrchestrator.getSimInfo(userId, subscriptionId); } diff --git a/apps/bff/src/modules/subscriptions/sim-management/services/sim-orchestrator.service.ts b/apps/bff/src/modules/subscriptions/sim-management/services/sim-orchestrator.service.ts index e3f6121a..0cbbf0c0 100644 --- a/apps/bff/src/modules/subscriptions/sim-management/services/sim-orchestrator.service.ts +++ b/apps/bff/src/modules/subscriptions/sim-management/services/sim-orchestrator.service.ts @@ -8,7 +8,8 @@ import { SimCancellationService } from "./sim-cancellation.service"; import { EsimManagementService } from "./esim-management.service"; import { SimValidationService } from "./sim-validation.service"; import { getErrorMessage } from "@bff/core/utils/error.util"; -import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; +import { simInfoSchema } from "@customer-portal/domain/sim"; +import type { SimInfo, SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim"; import type { SimTopUpRequest, SimPlanChangeRequest, @@ -113,10 +114,7 @@ export class SimOrchestratorService { async getSimInfo( userId: string, subscriptionId: number - ): Promise<{ - details: SimDetails; - usage: SimUsage; - }> { + ): Promise { try { const [details, usage] = await Promise.all([ this.getSimDetails(userId, subscriptionId), @@ -141,7 +139,7 @@ export class SimOrchestratorService { } } - return { details, usage }; + return simInfoSchema.parse({ details, usage }); } catch (error) { const sanitizedError = getErrorMessage(error); this.logger.error(`Failed to get comprehensive SIM info for subscription ${subscriptionId}`, { diff --git a/apps/bff/src/modules/subscriptions/subscriptions.controller.ts b/apps/bff/src/modules/subscriptions/subscriptions.controller.ts index de427f27..a6fe8d4b 100644 --- a/apps/bff/src/modules/subscriptions/subscriptions.controller.ts +++ b/apps/bff/src/modules/subscriptions/subscriptions.controller.ts @@ -33,6 +33,7 @@ import { simCancelRequestSchema, simFeaturesRequestSchema, simReissueRequestSchema, + type SimInfo, type SimTopupRequest, type SimChangePlanRequest, type SimCancelRequest, @@ -109,7 +110,7 @@ export class SubscriptionsController { async getSimInfo( @Request() req: RequestWithUser, @Param("id", ParseIntPipe) subscriptionId: number - ) { + ): Promise { return this.simManagementService.getSimInfo(req.user.id, subscriptionId); } diff --git a/apps/bff/src/modules/subscriptions/subscriptions.service.ts b/apps/bff/src/modules/subscriptions/subscriptions.service.ts index e9312a2f..77a42487 100644 --- a/apps/bff/src/modules/subscriptions/subscriptions.service.ts +++ b/apps/bff/src/modules/subscriptions/subscriptions.service.ts @@ -5,6 +5,7 @@ import { SubscriptionList, subscriptionListSchema, subscriptionStatusSchema, + subscriptionStatsSchema, type SubscriptionStatus, } from "@customer-portal/domain/subscriptions"; import type { Invoice, InvoiceItem, InvoiceList } from "@customer-portal/domain/billing"; @@ -201,7 +202,7 @@ export class SubscriptionsService { this.logger.log(`Generated subscription stats for user ${userId}`, stats); - return stats; + return subscriptionStatsSchema.parse(stats); } catch (error) { this.logger.error(`Failed to generate subscription stats for user ${userId}`, { error: getErrorMessage(error), diff --git a/apps/bff/src/modules/users/users.service.ts b/apps/bff/src/modules/users/users.service.ts index 83a2df27..213ca623 100644 --- a/apps/bff/src/modules/users/users.service.ts +++ b/apps/bff/src/modules/users/users.service.ts @@ -17,6 +17,7 @@ import { import type { Subscription } from "@customer-portal/domain/subscriptions"; import type { Invoice } from "@customer-portal/domain/billing"; import type { Activity, DashboardSummary, NextInvoice } from "@customer-portal/domain/dashboard"; +import { dashboardSummarySchema } from "@customer-portal/domain/dashboard"; import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service"; import { SalesforceService } from "@bff/integrations/salesforce/salesforce.service"; import { MappingsService } from "@bff/modules/id-mappings/mappings.service"; @@ -525,7 +526,7 @@ export class UsersService { nextInvoice, recentActivity, }; - return summary; + return dashboardSummarySchema.parse(summary); } catch (error) { this.logger.error(`Failed to get user summary for ${userId}`, { error: getErrorMessage(error), diff --git a/apps/portal/src/features/account/components/AddressCard.tsx b/apps/portal/src/features/account/components/AddressCard.tsx index 18331d52..215b1865 100644 --- a/apps/portal/src/features/account/components/AddressCard.tsx +++ b/apps/portal/src/features/account/components/AddressCard.tsx @@ -4,6 +4,7 @@ import { SubCard } from "@/components/molecules/SubCard/SubCard"; import { MapPinIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { AddressForm, type AddressFormProps } from "@/features/catalog/components"; import type { Address } from "@customer-portal/domain/customer"; +import { getCountryName } from "@/lib/constants/countries"; interface AddressCardProps { address: Address; @@ -26,6 +27,9 @@ export function AddressCard({ onSave, onAddressChange, }: AddressCardProps) { + const countryLabel = + address.country ? getCountryName(address.country) ?? address.country : null; + return (
@@ -88,7 +92,7 @@ export function AddressCard({ {[address.city, address.state, address.postcode].filter(Boolean).join(", ")}

)} - {address.country &&

{address.country}

} + {countryLabel &&

{countryLabel}

}
)} diff --git a/apps/portal/src/features/auth/components/SignupForm/AddressStep.tsx b/apps/portal/src/features/auth/components/SignupForm/AddressStep.tsx index 1dade10c..ae1e459e 100644 --- a/apps/portal/src/features/auth/components/SignupForm/AddressStep.tsx +++ b/apps/portal/src/features/auth/components/SignupForm/AddressStep.tsx @@ -11,29 +11,7 @@ import { FormField } from "@/components/molecules/FormField/FormField"; import type { FormErrors, FormTouched, UseZodFormReturn } from "@customer-portal/validation"; import type { SignupFormValues } from "./SignupForm"; import type { Address } from "@customer-portal/domain/customer"; - -const COUNTRIES = [ - { code: "US", name: "United States" }, - { code: "CA", name: "Canada" }, - { code: "GB", name: "United Kingdom" }, - { code: "AU", name: "Australia" }, - { code: "DE", name: "Germany" }, - { code: "FR", name: "France" }, - { code: "IT", name: "Italy" }, - { code: "ES", name: "Spain" }, - { code: "NL", name: "Netherlands" }, - { code: "SE", name: "Sweden" }, - { code: "NO", name: "Norway" }, - { code: "DK", name: "Denmark" }, - { code: "FI", name: "Finland" }, - { code: "CH", name: "Switzerland" }, - { code: "AT", name: "Austria" }, - { code: "BE", name: "Belgium" }, - { code: "IE", name: "Ireland" }, - { code: "PT", name: "Portugal" }, - { code: "GR", name: "Greece" }, - { code: "JP", name: "Japan" }, -]; +import { COUNTRY_OPTIONS } from "@/lib/constants/countries"; interface AddressStepProps { address: SignupFormValues["address"]; @@ -53,7 +31,19 @@ export function AddressStep({ // Use domain Address type directly - no type helpers needed const updateAddressField = useCallback( (field: keyof Address, value: string) => { - onAddressChange({ ...address, [field]: value }); + onAddressChange({ ...(address ?? {}), [field]: value }); + }, + [address, onAddressChange] + ); + + const handleCountryChange = useCallback( + (code: string) => { + const normalized = code || ""; + onAddressChange({ + ...(address ?? {}), + country: normalized, + countryCode: normalized, + }); }, [address, onAddressChange] ); @@ -139,13 +129,13 @@ export function AddressStep({