From 29366d6ae6b061ae9986108e4f536a2c003d1129 Mon Sep 17 00:00:00 2001
From: barsa
Date: Thu, 25 Sep 2025 18:59:07 +0900
Subject: [PATCH] Refactor BFF and portal components to enhance type
consistency and error handling. Update type definitions across Freebit,
WHMCS, and invoice management services, improving maintainability and
clarity. Streamline service methods and import paths, while cleaning up
unused code to ensure better organization throughout the project.
---
.../freebit/interfaces/freebit.types.ts | 9 +++-
.../services/freebit-operations.service.ts | 8 ++--
.../services/freebit-orchestrator.service.ts | 7 +--
.../salesforce/events/pubsub.subscriber.ts | 3 +-
.../salesforce/types/pubsub-events.types.ts | 4 +-
.../whmcs-connection-orchestrator.service.ts | 1 -
.../services/whmcs-error-handler.service.ts | 2 +-
.../services/whmcs-http-client.service.ts | 43 ++++++++++++++++-
.../whmcs/services/whmcs-invoice.service.ts | 17 +++----
.../services/invoice-transformer.service.ts | 6 +--
.../subscription-transformer.service.ts | 2 +-
.../whmcs-transformer-orchestrator.service.ts | 32 ++++++-------
.../whmcs/transformers/utils/data-utils.ts | 47 +++++++++++++++++--
apps/bff/src/main.ts | 1 +
.../modules/invoices/invoices.controller.ts | 4 +-
.../services/invoices-orchestrator.service.ts | 9 +---
.../subscriptions/sim-management.service.ts | 5 +-
.../subscriptions/subscriptions.service.ts | 2 +-
apps/bff/tsconfig.build.json | 3 +-
apps/portal/scripts/stubs/core-api.ts | 12 ++---
.../scripts/test-request-password-reset.cjs | 6 ++-
.../components/organisms/AppShell/Sidebar.tsx | 13 +++--
.../features/account/hooks/useAddressEdit.ts | 8 +---
.../features/account/hooks/useProfileEdit.ts | 16 +++----
.../LinkWhmcsForm/LinkWhmcsForm.tsx | 19 +++-----
.../auth/components/LoginForm/LoginForm.tsx | 11 ++---
.../PasswordResetForm/PasswordResetForm.tsx | 8 ++--
.../SetPasswordForm/SetPasswordForm.tsx | 3 +-
.../components/SignupForm/PasswordStep.tsx | 9 ++--
.../src/features/auth/services/auth.store.ts | 13 +++--
.../features/billing/views/PaymentMethods.tsx | 6 +--
.../steps/ServiceConfigurationStep.tsx | 4 +-
.../features/catalog/hooks/useSimConfigure.ts | 43 ++++++++++-------
.../orders/services/orders.service.ts | 12 ++++-
apps/portal/src/lib/api/index.ts | 6 +--
apps/portal/src/lib/api/runtime/client.ts | 25 +++++++---
apps/portal/src/lib/hooks/useLocalStorage.ts | 3 +-
apps/portal/src/lib/utils/error-handling.ts | 7 ++-
packages/domain/tsconfig.json | 4 +-
39 files changed, 265 insertions(+), 168 deletions(-)
diff --git a/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts b/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts
index b8ff72c4..62fc96bf 100644
--- a/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts
+++ b/apps/bff/src/integrations/freebit/interfaces/freebit.types.ts
@@ -92,6 +92,7 @@ export interface FreebitTopUpRequest {
quota: number; // KB units (e.g., 102400 for 100MB)
quotaCode?: string; // Campaign code
expire?: string; // YYYYMMDD format
+ runTime?: string; // Scheduled execution time (YYYYMMDDHHmm)
}
export interface FreebitTopUpResponse {
@@ -235,7 +236,13 @@ export interface FreebitCancelAccountResponse {
export interface FreebitEsimReissueRequest {
authKey: string;
- account: string;
+ requestDatas: Array<{
+ kind: "MVNO";
+ account: string;
+ newEid?: string;
+ oldEid?: string;
+ planCode?: string;
+ }>;
}
export interface FreebitEsimReissueResponse {
diff --git a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts
index 7be7f482..37a6393b 100644
--- a/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts
+++ b/apps/bff/src/integrations/freebit/services/freebit-operations.service.ts
@@ -144,7 +144,7 @@ export class FreebitOperationsService {
): Promise {
try {
const quotaKb = Math.round(quotaMb * 1024);
- const request: Omit = {
+ const baseRequest: Omit = {
account,
quota: quotaKb,
quotaCode: options.campaignCode,
@@ -153,9 +153,7 @@ export class FreebitOperationsService {
const scheduled = !!options.scheduledAt;
const endpoint = scheduled ? "/mvno/eachQuota/" : "/master/addSpec/";
- if (scheduled) {
- (request as any).runTime = options.scheduledAt;
- }
+ const request = scheduled ? { ...baseRequest, runTime: options.scheduledAt } : baseRequest;
await this.client.makeAuthenticatedRequest(
endpoint,
@@ -348,7 +346,7 @@ export class FreebitOperationsService {
try {
const request: Omit = {
requestDatas: [{ kind: "MVNO", account }],
- } as any;
+ };
await this.client.makeAuthenticatedRequest(
"/mvno/reissueEsim/",
diff --git a/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts b/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts
index 0b4fcdf9..1b1a10e6 100644
--- a/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts
+++ b/apps/bff/src/integrations/freebit/services/freebit-orchestrator.service.ts
@@ -1,12 +1,7 @@
import { Injectable } from "@nestjs/common";
import { FreebitOperationsService } from "./freebit-operations.service";
import { FreebitMapperService } from "./freebit-mapper.service";
-import type {
- SimDetails,
- SimUsage,
- SimTopUpHistory,
- FreebitEsimAddAccountRequest,
-} from "../interfaces/freebit.types";
+import type { SimDetails, SimUsage, SimTopUpHistory } from "../interfaces/freebit.types";
@Injectable()
export class FreebitOrchestratorService {
diff --git a/apps/bff/src/integrations/salesforce/events/pubsub.subscriber.ts b/apps/bff/src/integrations/salesforce/events/pubsub.subscriber.ts
index e58fb0d3..9f962de6 100644
--- a/apps/bff/src/integrations/salesforce/events/pubsub.subscriber.ts
+++ b/apps/bff/src/integrations/salesforce/events/pubsub.subscriber.ts
@@ -15,12 +15,13 @@ import type {
SalesforcePubSubError,
SalesforcePubSubSubscription,
SalesforcePubSubCallbackType,
+ SalesforcePubSubUnknownData,
} from "../types/pubsub-events.types";
type SubscribeCallback = (
subscription: SalesforcePubSubSubscription,
callbackType: SalesforcePubSubCallbackType,
- data: SalesforcePubSubEvent | SalesforcePubSubError | unknown
+ data: SalesforcePubSubEvent | SalesforcePubSubError | SalesforcePubSubUnknownData
) => void | Promise;
interface PubSubClient {
diff --git a/apps/bff/src/integrations/salesforce/types/pubsub-events.types.ts b/apps/bff/src/integrations/salesforce/types/pubsub-events.types.ts
index d4db9c62..948aaa35 100644
--- a/apps/bff/src/integrations/salesforce/types/pubsub-events.types.ts
+++ b/apps/bff/src/integrations/salesforce/types/pubsub-events.types.ts
@@ -33,8 +33,10 @@ export interface SalesforcePubSubError {
export type SalesforcePubSubCallbackType = "data" | "event" | "grpcstatus" | "end" | "error";
+export type SalesforcePubSubUnknownData = Record | null | undefined;
+
export interface SalesforcePubSubCallback {
subscription: SalesforcePubSubSubscription;
callbackType: SalesforcePubSubCallbackType;
- data: SalesforcePubSubEvent | SalesforcePubSubError | unknown;
+ data: SalesforcePubSubEvent | SalesforcePubSubError | SalesforcePubSubUnknownData;
}
diff --git a/apps/bff/src/integrations/whmcs/connection/services/whmcs-connection-orchestrator.service.ts b/apps/bff/src/integrations/whmcs/connection/services/whmcs-connection-orchestrator.service.ts
index 6a504ad7..ea622763 100644
--- a/apps/bff/src/integrations/whmcs/connection/services/whmcs-connection-orchestrator.service.ts
+++ b/apps/bff/src/integrations/whmcs/connection/services/whmcs-connection-orchestrator.service.ts
@@ -6,7 +6,6 @@ import { WhmcsHttpClientService } from "./whmcs-http-client.service";
import { WhmcsErrorHandlerService } from "./whmcs-error-handler.service";
import { WhmcsApiMethodsService } from "./whmcs-api-methods.service";
import type {
- WhmcsApiResponse,
WhmcsErrorResponse,
WhmcsAddClientParams,
WhmcsValidateLoginParams,
diff --git a/apps/bff/src/integrations/whmcs/connection/services/whmcs-error-handler.service.ts b/apps/bff/src/integrations/whmcs/connection/services/whmcs-error-handler.service.ts
index ead43d6e..7b9fdab9 100644
--- a/apps/bff/src/integrations/whmcs/connection/services/whmcs-error-handler.service.ts
+++ b/apps/bff/src/integrations/whmcs/connection/services/whmcs-error-handler.service.ts
@@ -44,7 +44,7 @@ export class WhmcsErrorHandlerService {
/**
* Handle general request errors (network, timeout, etc.)
*/
- handleRequestError(error: unknown, action: string, params: Record): never {
+ handleRequestError(error: unknown, action: string, _params: Record): never {
const message = getErrorMessage(error);
if (this.isTimeoutError(error)) {
diff --git a/apps/bff/src/integrations/whmcs/connection/services/whmcs-http-client.service.ts b/apps/bff/src/integrations/whmcs/connection/services/whmcs-http-client.service.ts
index 17f0a8e4..7368b9f9 100644
--- a/apps/bff/src/integrations/whmcs/connection/services/whmcs-http-client.service.ts
+++ b/apps/bff/src/integrations/whmcs/connection/services/whmcs-http-client.service.ts
@@ -189,14 +189,53 @@ export class WhmcsHttpClientService {
// Add parameters
for (const [key, value] of Object.entries(params)) {
- if (value !== undefined && value !== null) {
- formData.append(key, String(value));
+ if (value === undefined || value === null) {
+ continue;
}
+
+ const serialized = this.serializeParamValue(value);
+ formData.append(key, serialized);
}
return formData.toString();
}
+ private serializeParamValue(value: unknown): string {
+ if (typeof value === "string") {
+ return value;
+ }
+
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
+ return String(value);
+ }
+
+ if (value instanceof Date) {
+ return value.toISOString();
+ }
+
+ if (Array.isArray(value)) {
+ return value.map(entry => this.serializeParamValue(entry)).join(",");
+ }
+
+ if (typeof value === "object" && value !== null) {
+ try {
+ return JSON.stringify(value);
+ } catch {
+ return Object.prototype.toString.call(value);
+ }
+ }
+
+ if (typeof value === "symbol") {
+ return value.description ? `Symbol(${value.description})` : "Symbol()";
+ }
+
+ if (typeof value === "function") {
+ return value.name ? `[Function ${value.name}]` : "[Function anonymous]";
+ }
+
+ return Object.prototype.toString.call(value);
+ }
+
/**
* Parse WHMCS API response
*/
diff --git a/apps/bff/src/integrations/whmcs/services/whmcs-invoice.service.ts b/apps/bff/src/integrations/whmcs/services/whmcs-invoice.service.ts
index 14b66704..24f9904c 100644
--- a/apps/bff/src/integrations/whmcs/services/whmcs-invoice.service.ts
+++ b/apps/bff/src/integrations/whmcs/services/whmcs-invoice.service.ts
@@ -4,7 +4,7 @@ import { Injectable, NotFoundException, Inject } from "@nestjs/common";
import { Invoice, InvoiceList } from "@customer-portal/domain";
import {
invoiceListSchema,
- invoiceSchema as invoiceEntitySchema,
+ invoiceSchema,
} from "@customer-portal/domain/validation/shared/entities";
import { WhmcsConnectionOrchestratorService } from "../connection/services/whmcs-connection-orchestrator.service";
import { InvoiceTransformerService } from "../transformers/services/invoice-transformer.service";
@@ -15,9 +15,6 @@ import {
WhmcsCreateInvoiceParams,
WhmcsUpdateInvoiceParams,
WhmcsCapturePaymentParams,
- WhmcsCreateInvoiceResponse,
- WhmcsUpdateInvoiceResponse,
- WhmcsCapturePaymentResponse,
} from "../types/whmcs-api.types";
export interface InvoiceFilters {
@@ -88,7 +85,7 @@ export class WhmcsInvoiceService {
this.logger.log(
`Fetched ${result.invoices.length} invoices for client ${clientId}, page ${page}`
);
- return result;
+ return result as InvoiceList;
} catch (error) {
this.logger.error(`Failed to fetch invoices for client ${clientId}`, {
error: getErrorMessage(error),
@@ -117,7 +114,7 @@ export class WhmcsInvoiceService {
try {
// Get detailed invoice with items
const detailedInvoice = await this.getInvoiceById(clientId, userId, invoice.id);
- const parseResult = invoiceEntitySchema.safeParse(detailedInvoice);
+ const parseResult = invoiceSchema.safeParse(detailedInvoice);
if (!parseResult.success) {
this.logger.error("Failed to parse detailed invoice", {
error: parseResult.error.issues,
@@ -139,7 +136,7 @@ export class WhmcsInvoiceService {
);
const result: InvoiceList = {
- invoices: invoicesWithItems,
+ invoices: invoicesWithItems as Invoice[],
pagination: invoiceList.pagination,
};
@@ -184,7 +181,7 @@ export class WhmcsInvoiceService {
// Transform invoice
const invoice = this.invoiceTransformer.transformInvoice(response);
- const parseResult = invoiceEntitySchema.safeParse(invoice);
+ const parseResult = invoiceSchema.safeParse(invoice);
if (!parseResult.success) {
throw new Error(`Invalid invoice data after transformation`);
}
@@ -232,8 +229,8 @@ export class WhmcsInvoiceService {
for (const whmcsInvoice of response.invoices.invoice) {
try {
const transformed = this.invoiceTransformer.transformInvoice(whmcsInvoice);
- const parsed = invoiceEntitySchema.parse(transformed);
- invoices.push(parsed);
+ const parsed = invoiceSchema.parse(transformed);
+ invoices.push(parsed as Invoice);
} catch (error) {
this.logger.error(`Failed to transform invoice ${whmcsInvoice.id}`, {
error: getErrorMessage(error),
diff --git a/apps/bff/src/integrations/whmcs/transformers/services/invoice-transformer.service.ts b/apps/bff/src/integrations/whmcs/transformers/services/invoice-transformer.service.ts
index cbbc03e3..0c8a2595 100644
--- a/apps/bff/src/integrations/whmcs/transformers/services/invoice-transformer.service.ts
+++ b/apps/bff/src/integrations/whmcs/transformers/services/invoice-transformer.service.ts
@@ -1,11 +1,7 @@
import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { Invoice, InvoiceItem as BaseInvoiceItem } from "@customer-portal/domain";
-import type {
- WhmcsInvoice,
- WhmcsInvoiceItems,
- WhmcsCustomField,
-} from "../../types/whmcs-api.types";
+import type { WhmcsInvoice, WhmcsInvoiceItems } from "../../types/whmcs-api.types";
import { DataUtils } from "../utils/data-utils";
import { StatusNormalizer } from "../utils/status-normalizer";
import { TransformationValidator } from "../validators/transformation-validator";
diff --git a/apps/bff/src/integrations/whmcs/transformers/services/subscription-transformer.service.ts b/apps/bff/src/integrations/whmcs/transformers/services/subscription-transformer.service.ts
index 43248b4b..01b4bbba 100644
--- a/apps/bff/src/integrations/whmcs/transformers/services/subscription-transformer.service.ts
+++ b/apps/bff/src/integrations/whmcs/transformers/services/subscription-transformer.service.ts
@@ -131,7 +131,7 @@ export class SubscriptionTransformerService {
private normalizeFieldName(name: string): string {
return name
.toLowerCase()
- .replace(/[^a-z0-9]+(.)/g, (_, char) => char.toUpperCase())
+ .replace(/[^a-z0-9]+(.)/g, (_match: string, char: string) => char.toUpperCase())
.replace(/^[^a-z]+/, "");
}
diff --git a/apps/bff/src/integrations/whmcs/transformers/services/whmcs-transformer-orchestrator.service.ts b/apps/bff/src/integrations/whmcs/transformers/services/whmcs-transformer-orchestrator.service.ts
index 48f4d849..3721fbdf 100644
--- a/apps/bff/src/integrations/whmcs/transformers/services/whmcs-transformer-orchestrator.service.ts
+++ b/apps/bff/src/integrations/whmcs/transformers/services/whmcs-transformer-orchestrator.service.ts
@@ -30,7 +30,7 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform WHMCS invoice to our standard Invoice format
*/
- async transformInvoice(whmcsInvoice: WhmcsInvoice): Promise {
+ transformInvoice(whmcsInvoice: WhmcsInvoice): Invoice {
try {
return this.invoiceTransformer.transformInvoice(whmcsInvoice);
} catch (error) {
@@ -60,7 +60,7 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform WHMCS product/service to our standard Subscription format
*/
- async transformSubscription(whmcsProduct: WhmcsProduct): Promise {
+ transformSubscription(whmcsProduct: WhmcsProduct): Subscription {
try {
return this.subscriptionTransformer.transformSubscription(whmcsProduct);
} catch (error) {
@@ -90,7 +90,7 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform WHMCS payment gateway to shared PaymentGateway interface
*/
- async transformPaymentGateway(whmcsGateway: WhmcsPaymentGateway): Promise {
+ transformPaymentGateway(whmcsGateway: WhmcsPaymentGateway): PaymentGateway {
try {
return this.paymentTransformer.transformPaymentGateway(whmcsGateway);
} catch (error) {
@@ -120,7 +120,7 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform WHMCS payment method to shared PaymentMethod interface
*/
- async transformPaymentMethod(whmcsPayMethod: WhmcsPaymentMethod): Promise {
+ transformPaymentMethod(whmcsPayMethod: WhmcsPaymentMethod): PaymentMethod {
try {
return this.paymentTransformer.transformPaymentMethod(whmcsPayMethod);
} catch (error) {
@@ -150,16 +150,16 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform multiple invoices in batch with error handling
*/
- async transformInvoices(whmcsInvoices: WhmcsInvoice[]): Promise<{
+ transformInvoices(whmcsInvoices: WhmcsInvoice[]): {
successful: Invoice[];
failed: Array<{ invoice: WhmcsInvoice; error: string }>;
- }> {
+ } {
const successful: Invoice[] = [];
const failed: Array<{ invoice: WhmcsInvoice; error: string }> = [];
for (const whmcsInvoice of whmcsInvoices) {
try {
- const transformed = await this.transformInvoice(whmcsInvoice);
+ const transformed = this.transformInvoice(whmcsInvoice);
successful.push(transformed);
} catch (error) {
failed.push({
@@ -181,16 +181,16 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform multiple subscriptions in batch with error handling
*/
- async transformSubscriptions(whmcsProducts: WhmcsProduct[]): Promise<{
+ transformSubscriptions(whmcsProducts: WhmcsProduct[]): {
successful: Subscription[];
failed: Array<{ product: WhmcsProduct; error: string }>;
- }> {
+ } {
const successful: Subscription[] = [];
const failed: Array<{ product: WhmcsProduct; error: string }> = [];
for (const whmcsProduct of whmcsProducts) {
try {
- const transformed = await this.transformSubscription(whmcsProduct);
+ const transformed = this.transformSubscription(whmcsProduct);
successful.push(transformed);
} catch (error) {
failed.push({
@@ -212,16 +212,16 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform multiple payment methods in batch with error handling
*/
- async transformPaymentMethods(whmcsPayMethods: WhmcsPaymentMethod[]): Promise<{
+ transformPaymentMethods(whmcsPayMethods: WhmcsPaymentMethod[]): {
successful: PaymentMethod[];
failed: Array<{ payMethod: WhmcsPaymentMethod; error: string }>;
- }> {
+ } {
const successful: PaymentMethod[] = [];
const failed: Array<{ payMethod: WhmcsPaymentMethod; error: string }> = [];
for (const whmcsPayMethod of whmcsPayMethods) {
try {
- const transformed = await this.transformPaymentMethod(whmcsPayMethod);
+ const transformed = this.transformPaymentMethod(whmcsPayMethod);
successful.push(transformed);
} catch (error) {
failed.push({
@@ -243,16 +243,16 @@ export class WhmcsTransformerOrchestratorService {
/**
* Transform multiple payment gateways in batch with error handling
*/
- async transformPaymentGateways(whmcsGateways: WhmcsPaymentGateway[]): Promise<{
+ transformPaymentGateways(whmcsGateways: WhmcsPaymentGateway[]): {
successful: PaymentGateway[];
failed: Array<{ gateway: WhmcsPaymentGateway; error: string }>;
- }> {
+ } {
const successful: PaymentGateway[] = [];
const failed: Array<{ gateway: WhmcsPaymentGateway; error: string }> = [];
for (const whmcsGateway of whmcsGateways) {
try {
- const transformed = await this.transformPaymentGateway(whmcsGateway);
+ const transformed = this.transformPaymentGateway(whmcsGateway);
successful.push(transformed);
} catch (error) {
failed.push({
diff --git a/apps/bff/src/integrations/whmcs/transformers/utils/data-utils.ts b/apps/bff/src/integrations/whmcs/transformers/utils/data-utils.ts
index 43a23aad..d119b353 100644
--- a/apps/bff/src/integrations/whmcs/transformers/utils/data-utils.ts
+++ b/apps/bff/src/integrations/whmcs/transformers/utils/data-utils.ts
@@ -126,18 +126,59 @@ export class DataUtils {
if (!customFields) return undefined;
// Try exact match first
- if (customFields[fieldName]) {
- return String(customFields[fieldName]);
+ const directValue = DataUtils.toStringValue(customFields[fieldName]);
+ if (directValue !== undefined) {
+ return directValue;
}
// Try case-insensitive match
const lowerFieldName = fieldName.toLowerCase();
for (const [key, value] of Object.entries(customFields)) {
if (key.toLowerCase() === lowerFieldName) {
- return String(value);
+ return DataUtils.toStringValue(value);
}
}
return undefined;
}
+
+ private static toStringValue(value: unknown): string | undefined {
+ if (value === undefined || value === null) {
+ return undefined;
+ }
+
+ if (typeof value === "string") {
+ return value;
+ }
+
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
+ return String(value);
+ }
+
+ if (value instanceof Date) {
+ return value.toISOString();
+ }
+
+ if (Array.isArray(value)) {
+ return value.map(entry => DataUtils.toStringValue(entry) ?? "").join(",");
+ }
+
+ if (typeof value === "object") {
+ try {
+ return JSON.stringify(value);
+ } catch {
+ return Object.prototype.toString.call(value);
+ }
+ }
+
+ if (typeof value === "symbol") {
+ return value.description ? `Symbol(${value.description})` : "Symbol()";
+ }
+
+ if (typeof value === "function") {
+ return value.name ? `[Function ${value.name}]` : "[Function anonymous]";
+ }
+
+ return Object.prototype.toString.call(value);
+ }
}
diff --git a/apps/bff/src/main.ts b/apps/bff/src/main.ts
index f08a3bca..283f2d5d 100644
--- a/apps/bff/src/main.ts
+++ b/apps/bff/src/main.ts
@@ -1,3 +1,4 @@
+import "tsconfig-paths/register";
import { Logger, type INestApplication } from "@nestjs/common";
import { bootstrap } from "./app/bootstrap";
diff --git a/apps/bff/src/modules/invoices/invoices.controller.ts b/apps/bff/src/modules/invoices/invoices.controller.ts
index 360ccc32..cc57346e 100644
--- a/apps/bff/src/modules/invoices/invoices.controller.ts
+++ b/apps/bff/src/modules/invoices/invoices.controller.ts
@@ -173,10 +173,10 @@ export class InvoicesController {
@ApiParam({ name: "id", type: Number, description: "Invoice ID" })
@ApiOkResponse({ description: "List of related subscriptions" })
@ApiResponse({ status: 404, description: "Invoice not found" })
- async getInvoiceSubscriptions(
+ getInvoiceSubscriptions(
@Request() req: AuthenticatedRequest,
@Param("id", ParseIntPipe) invoiceId: number
- ): Promise {
+ ): Subscription[] {
if (invoiceId <= 0) {
throw new BadRequestException("Invoice ID must be a positive number");
}
diff --git a/apps/bff/src/modules/invoices/services/invoices-orchestrator.service.ts b/apps/bff/src/modules/invoices/services/invoices-orchestrator.service.ts
index 267f2e16..d3ded4ff 100644
--- a/apps/bff/src/modules/invoices/services/invoices-orchestrator.service.ts
+++ b/apps/bff/src/modules/invoices/services/invoices-orchestrator.service.ts
@@ -1,13 +1,6 @@
import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
-import {
- Invoice,
- InvoiceList,
- InvoiceSsoLink,
- InvoicePaymentLink,
- PaymentMethodList,
- PaymentGatewayList,
-} from "@customer-portal/domain";
+import { Invoice, InvoiceList } from "@customer-portal/domain";
import { InvoiceRetrievalService } from "./invoice-retrieval.service";
import { InvoiceHealthService } from "./invoice-health.service";
import { InvoiceValidatorService } from "../validators/invoice-validator.service";
diff --git a/apps/bff/src/modules/subscriptions/sim-management.service.ts b/apps/bff/src/modules/subscriptions/sim-management.service.ts
index b63ab6b5..a1fc6bad 100644
--- a/apps/bff/src/modules/subscriptions/sim-management.service.ts
+++ b/apps/bff/src/modules/subscriptions/sim-management.service.ts
@@ -13,6 +13,7 @@ import type {
SimTopUpHistoryRequest,
SimFeaturesUpdateRequest,
} from "./sim-management/types/sim-requests.types";
+import type { SimNotificationContext } from "./sim-management/interfaces/sim-base.interface";
@Injectable()
export class SimManagementService {
@@ -25,9 +26,9 @@ export class SimManagementService {
private async notifySimAction(
action: string,
status: "SUCCESS" | "ERROR",
- context: Record
+ context: SimNotificationContext
): Promise {
- return this.simNotification.notifySimAction(action, status, context as any);
+ return this.simNotification.notifySimAction(action, status, context);
}
/**
diff --git a/apps/bff/src/modules/subscriptions/subscriptions.service.ts b/apps/bff/src/modules/subscriptions/subscriptions.service.ts
index 4b750f2d..89bd7aef 100644
--- a/apps/bff/src/modules/subscriptions/subscriptions.service.ts
+++ b/apps/bff/src/modules/subscriptions/subscriptions.service.ts
@@ -6,7 +6,7 @@ import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { Logger } from "nestjs-pino";
import { z } from "zod";
-import { subscriptionSchema } from "@customer-portal/domain/validation/shared/entities";
+import { subscriptionSchema } from "@customer-portal/domain";
import type {
WhmcsProduct,
WhmcsProductsResponse,
diff --git a/apps/bff/tsconfig.build.json b/apps/bff/tsconfig.build.json
index b0b3023c..3796e8d6 100644
--- a/apps/bff/tsconfig.build.json
+++ b/apps/bff/tsconfig.build.json
@@ -13,7 +13,6 @@
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test", "**/*.spec.ts"],
"references": [
- { "path": "../../packages/domain" },
- { "path": "../../packages/validation" }
+ { "path": "../../packages/domain" }
]
}
diff --git a/apps/portal/scripts/stubs/core-api.ts b/apps/portal/scripts/stubs/core-api.ts
index df6d3690..a21dca65 100644
--- a/apps/portal/scripts/stubs/core-api.ts
+++ b/apps/portal/scripts/stubs/core-api.ts
@@ -3,14 +3,14 @@ export type PostCall = [path: string, options?: unknown];
export const postCalls: PostCall[] = [];
export const apiClient = {
- POST: async (path: string, options?: unknown) => {
+ POST: (path: string, options?: unknown) => {
postCalls.push([path, options]);
- return { data: null } as const;
+ return Promise.resolve({ data: null } as const);
},
- GET: async () => ({ data: null }) as const,
- PUT: async () => ({ data: null }) as const,
- PATCH: async () => ({ data: null }) as const,
- DELETE: async () => ({ data: null }) as const,
+ GET: () => Promise.resolve({ data: null } as const),
+ PUT: () => Promise.resolve({ data: null } as const),
+ PATCH: () => Promise.resolve({ data: null } as const),
+ DELETE: () => Promise.resolve({ data: null } as const),
};
export const configureApiClientAuth = () => undefined;
diff --git a/apps/portal/scripts/test-request-password-reset.cjs b/apps/portal/scripts/test-request-password-reset.cjs
index 9f2e75b3..784cb2be 100755
--- a/apps/portal/scripts/test-request-password-reset.cjs
+++ b/apps/portal/scripts/test-request-password-reset.cjs
@@ -1,4 +1,6 @@
#!/usr/bin/env node
+/* eslint-env node */
+/* global __dirname, console, process */
const fs = require("node:fs");
const path = require("node:path");
@@ -94,7 +96,7 @@ const { useAuthStore } = require("../src/features/auth/services/auth.store.ts");
const [endpoint, options] = coreApiStub.postCalls[0];
if (endpoint !== "/auth/request-password-reset") {
throw new Error(
- `Expected endpoint \"/auth/request-password-reset\" but received \"${endpoint}\"`
+ `Expected endpoint "/auth/request-password-reset" but received "${endpoint}"`
);
}
@@ -109,7 +111,7 @@ const { useAuthStore } = require("../src/features/auth/services/auth.store.ts");
if (body.email !== payload.email) {
throw new Error(
- `Expected request body email to be \"${payload.email}\" but received \"${body.email}\"`
+ `Expected request body email to be "${payload.email}" but received "${body.email}"`
);
}
diff --git a/apps/portal/src/components/organisms/AppShell/Sidebar.tsx b/apps/portal/src/components/organisms/AppShell/Sidebar.tsx
index 5adc3ec9..2b2ab08b 100644
--- a/apps/portal/src/components/organisms/AppShell/Sidebar.tsx
+++ b/apps/portal/src/components/organisms/AppShell/Sidebar.tsx
@@ -140,10 +140,9 @@ const NavigationItem = memo(function NavigationItem({
href={child.href}
prefetch
onMouseEnter={() => {
- try {
- // Warm up code/data for faster clicks
- if (child.href) router.prefetch(child.href);
- } catch {}
+ if (child.href) {
+ void router.prefetch(child.href);
+ }
}}
className={`group flex items-center px-3 py-2 text-sm rounded-md transition-all duration-200 relative ${
isChildActive
@@ -194,9 +193,9 @@ const NavigationItem = memo(function NavigationItem({
href={item.href || "#"}
prefetch
onMouseEnter={() => {
- try {
- if (item.href && item.href !== "#") router.prefetch(item.href);
- } catch {}
+ if (item.href && item.href !== "#") {
+ void router.prefetch(item.href);
+ }
}}
className={`group w-full flex items-center px-3 py-2.5 text-sm font-medium rounded-lg transition-all duration-200 relative ${
isActive
diff --git a/apps/portal/src/features/account/hooks/useAddressEdit.ts b/apps/portal/src/features/account/hooks/useAddressEdit.ts
index e696b13e..ccd7b46e 100644
--- a/apps/portal/src/features/account/hooks/useAddressEdit.ts
+++ b/apps/portal/src/features/account/hooks/useAddressEdit.ts
@@ -11,12 +11,8 @@ import { useZodForm } from "@customer-portal/validation";
export function useAddressEdit(initial: AddressFormData) {
const handleSave = useCallback(async (formData: AddressFormData) => {
- try {
- const requestData = addressFormToRequest(formData);
- await accountService.updateAddress(requestData);
- } catch (error) {
- throw error; // Let useZodForm handle the error state
- }
+ const requestData = addressFormToRequest(formData);
+ await accountService.updateAddress(requestData);
}, []);
return useZodForm({
diff --git a/apps/portal/src/features/account/hooks/useProfileEdit.ts b/apps/portal/src/features/account/hooks/useProfileEdit.ts
index aa8539e8..32e9f043 100644
--- a/apps/portal/src/features/account/hooks/useProfileEdit.ts
+++ b/apps/portal/src/features/account/hooks/useProfileEdit.ts
@@ -12,17 +12,13 @@ import { useZodForm } from "@customer-portal/validation";
export function useProfileEdit(initial: ProfileEditFormData) {
const handleSave = useCallback(async (formData: ProfileEditFormData) => {
- try {
- const requestData = profileFormToRequest(formData);
- const updated = await accountService.updateProfile(requestData);
+ const requestData = profileFormToRequest(formData);
+ const updated = await accountService.updateProfile(requestData);
- useAuthStore.setState(state => ({
- ...state,
- user: state.user ? { ...state.user, ...updated } : state.user,
- }));
- } catch (error) {
- throw error; // Let useZodForm handle the error state
- }
+ useAuthStore.setState(state => ({
+ ...state,
+ user: state.user ? { ...state.user, ...updated } : state.user,
+ }));
}, []);
return useZodForm({
diff --git a/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx b/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx
index 2eb34c95..7317d7ce 100644
--- a/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx
+++ b/apps/portal/src/features/auth/components/LinkWhmcsForm/LinkWhmcsForm.tsx
@@ -22,17 +22,12 @@ export function LinkWhmcsForm({ onTransferred, className = "" }: LinkWhmcsFormPr
const handleLink = useCallback(
async (formData: LinkWhmcsFormData) => {
clearError();
- try {
- const payload: LinkWhmcsRequestInput = {
- email: formData.email,
- password: formData.password,
- };
- const result = await linkWhmcs(payload);
- onTransferred?.({ ...result, email: formData.email });
- } catch (err) {
- // Error is handled by useZodForm
- throw err;
- }
+ const payload: LinkWhmcsRequestInput = {
+ email: formData.email,
+ password: formData.password,
+ };
+ const result = await linkWhmcs(payload);
+ onTransferred?.({ ...result, email: formData.email });
},
[linkWhmcs, onTransferred, clearError]
);
@@ -56,7 +51,7 @@ export function LinkWhmcsForm({ onTransferred, className = "" }: LinkWhmcsFormPr
-