Refactor integration services and update import paths to align with the new domain structure, enhancing type safety and maintainability. Streamline Freebit integration by utilizing updated provider methods and removing deprecated types. Improve organization and consistency in data handling across various modules, including catalog and billing services.

This commit is contained in:
barsa 2025-10-03 17:08:42 +09:00
parent 69aa47ad59
commit 12c3dc976f
230 changed files with 191 additions and 5278 deletions

View File

@ -1,4 +1,4 @@
import type { SalesforceProductFieldMap } from "@customer-portal/domain";
import type { SalesforceProductFieldMap } from "@customer-portal/domain/billing";
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";

View File

@ -6,7 +6,7 @@ import type {
FreebitConfig,
FreebitAuthRequest,
FreebitAuthResponse,
} from "../interfaces/freebit.types";
} from "@customer-portal/domain/sim/providers/freebit";
import { FreebitError } from "./freebit-error.service";
@Injectable()

View File

@ -26,7 +26,6 @@ import type {
FreebitAccountDetailsRequest,
FreebitTrafficInfoRequest,
FreebitQuotaHistoryRequest,
FreebitQuotaHistoryResponse,
FreebitEsimAddAccountRequest,
} from "@customer-portal/domain/sim/providers/freebit";
import type { SimDetails, SimTopUpHistory, SimUsage } from "@customer-portal/domain/sim";
@ -200,7 +199,7 @@ export class FreebitOperationsService {
const response = await this.client.makeAuthenticatedRequest<
FreebitQuotaHistoryResponse,
typeof request
FreebitQuotaHistoryRequest
>("/mvno/getQuotaHistory/", request);
return this.mapper.mapToSimTopUpHistory(response, account);

View File

@ -9,7 +9,7 @@ import {
type AccountData,
type UpsertResult,
} from "./services/salesforce-account.service";
import type { SalesforceAccountRecord, SalesforceOrderRecord } from "@customer-portal/domain";
import type { SalesforceAccountRecord, SalesforceOrderRecord } from "@customer-portal/domain/billing";
/**
* Clean Salesforce Service - Only includes actually used functionality

View File

@ -2,7 +2,7 @@ import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util";
import { SalesforceConnection } from "./salesforce-connection.service";
import type { SalesforceAccountRecord, SalesforceQueryResult } from "@customer-portal/domain";
import type { SalesforceAccountRecord, SalesforceQueryResult } from "@customer-portal/domain/billing";
export interface AccountData {
name: string;

View File

@ -6,7 +6,7 @@ import type {
SimCatalogProduct,
SimActivationFeeCatalogItem,
VpnCatalogProduct,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { InternetCatalogService } from "./services/internet-catalog.service";
import { SimCatalogService } from "./services/sim-catalog.service";
import { VpnCatalogService } from "./services/vpn-catalog.service";

View File

@ -12,7 +12,7 @@ import type {
SalesforceProduct2WithPricebookEntries,
SalesforcePricebookEntryRecord,
SalesforceQueryResult,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
@Injectable()
export class BaseCatalogService {

View File

@ -6,7 +6,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { SalesforceConnection } from "@bff/integrations/salesforce/services/salesforce-connection.service";
import { SalesforceFieldMapService } from "@bff/core/config/field-map";

View File

@ -6,7 +6,7 @@ import type {
SalesforceProduct2WithPricebookEntries,
SimCatalogProduct,
SimActivationFeeCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import {
mapSimProduct,
mapSimActivationFee,

View File

@ -7,7 +7,7 @@ import { BaseCatalogService } from "./base-catalog.service";
import type {
SalesforceProduct2WithPricebookEntries,
VpnCatalogProduct,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { mapVpnProduct } from "@bff/modules/catalog/utils/salesforce-product.mapper";
@Injectable()

View File

@ -7,11 +7,11 @@ import type {
SimActivationFeeCatalogItem,
SimCatalogProduct,
VpnCatalogProduct,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type {
SalesforceProduct2WithPricebookEntries,
SalesforcePricebookEntryRecord,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type { SalesforceFieldMap } from "@bff/core/config/field-map";
export type SalesforceCatalogProductRecord = SalesforceProduct2WithPricebookEntries;

View File

@ -25,8 +25,8 @@ import type {
PaymentGatewayList,
InvoicePaymentLink,
InvoiceListQuery,
} from "@customer-portal/domain";
import { invoiceListQuerySchema } from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { invoiceListQuerySchema } from "@customer-portal/domain/billing";
interface AuthenticatedRequest {
user: { id: string };

View File

@ -5,7 +5,7 @@ import {
Inject,
} from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { Invoice, InvoiceList } from "@customer-portal/domain";
import { Invoice, InvoiceList } from "@customer-portal/domain/billing";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { getErrorMessage } from "@bff/core/utils/error.util";

View File

@ -1,6 +1,6 @@
import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { Invoice, InvoiceList } from "@customer-portal/domain";
import { Invoice, InvoiceList } from "@customer-portal/domain/billing";
import { InvoiceRetrievalService } from "./invoice-retrieval.service";
import { InvoiceHealthService } from "./invoice-health.service";
import { InvoiceValidatorService } from "../validators/invoice-validator.service";

View File

@ -15,7 +15,7 @@ import {
updateAddressRequestSchema,
type UpdateProfileRequest,
type UpdateAddressRequest,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type { RequestWithUser } from "@bff/modules/auth/auth.types";
@Controller("me")

View File

@ -8,7 +8,7 @@ import { accountService } from "@/features/account/services/account.service";
import { Sidebar } from "./Sidebar";
import { Header } from "./Header";
import { computeNavigation } from "./navigation";
import type { Subscription } from "@customer-portal/domain";
import type { Subscription } from "@customer-portal/domain/billing";
interface AppShellProps {
children: React.ReactNode;

View File

@ -1,4 +1,4 @@
import type { Subscription } from "@customer-portal/domain";
import type { Subscription } from "@customer-portal/domain/billing";
import type { ReactNode } from "react";
import {
HomeIcon,

View File

@ -3,7 +3,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";
import type { Address } from "@customer-portal/domain/billing";
interface AddressCardProps {
address: Address;

View File

@ -2,7 +2,7 @@
import { SubCard } from "@/components/molecules/SubCard/SubCard";
import { UserIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline";
import type { ProfileDisplayData } from "@customer-portal/domain";
import type { ProfileDisplayData } from "@customer-portal/domain/billing";
interface PersonalInfoCardProps {
data: ProfileDisplayData;

View File

@ -6,7 +6,7 @@ import {
addressFormSchema,
addressFormToRequest,
type AddressFormData,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { useZodForm } from "@customer-portal/validation";
export function useAddressEdit(initial: AddressFormData) {

View File

@ -6,10 +6,10 @@ import { accountService } from "@/features/account/services/account.service";
import { logger } from "@customer-portal/logging";
// Use centralized profile types
import type { ProfileEditFormData } from "@customer-portal/domain";
import type { ProfileEditFormData } from "@customer-portal/domain/billing";
// Address type moved to domain package
import type { Address } from "@customer-portal/domain";
import type { Address } from "@customer-portal/domain/billing";
export function useProfileData() {
const { user } = useAuthStore();

View File

@ -7,7 +7,7 @@ import {
profileEditFormSchema,
profileFormToRequest,
type ProfileEditFormData,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { useZodForm } from "@customer-portal/validation";
export function useProfileEdit(initial: ProfileEditFormData) {

View File

@ -1,5 +1,5 @@
import { apiClient, getDataOrThrow, getNullableData } from "@/lib/api";
import type { Address, UserProfile } from "@customer-portal/domain";
import type { Address, UserProfile } from "@customer-portal/domain/billing";
type ProfileUpdateInput = {
firstName?: string;

View File

@ -10,7 +10,7 @@ import Link from "next/link";
import { Button, Input, ErrorMessage } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField";
import { useLogin } from "../../hooks/use-auth";
import { loginFormSchema, loginFormToRequest } from "@customer-portal/domain";
import { loginFormSchema, loginFormToRequest } from "@customer-portal/domain/billing";
import { useZodForm } from "@customer-portal/validation";
import { z } from "zod";

View File

@ -16,7 +16,7 @@ import {
passwordResetFormSchema,
type PasswordResetRequestFormData,
type PasswordResetFormData,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { z } from "zod";
interface PasswordResetFormProps {

View File

@ -11,7 +11,7 @@ import { Button, Input, ErrorMessage } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField";
import { useWhmcsLink } from "../../hooks/use-auth";
import { useZodForm } from "@customer-portal/validation";
import { setPasswordFormSchema, type SetPasswordFormData } from "@customer-portal/domain";
import { setPasswordFormSchema, type SetPasswordFormData } from "@customer-portal/domain/billing";
import { z } from "zod";
interface SetPasswordFormProps {

View File

@ -10,7 +10,7 @@ import {
} from "@heroicons/react/24/outline";
import { StatusPill } from "@/components/atoms/status-pill";
import type { StatusPillProps } from "@/components/atoms/status-pill";
import type { InvoiceStatus } from "@customer-portal/domain";
import type { InvoiceStatus } from "@customer-portal/domain/billing";
interface BillingStatusBadgeProps extends Omit<StatusPillProps, "variant" | "icon" | "label"> {
status: string;

View File

@ -10,8 +10,8 @@ import {
ArrowRightIcon,
} from "@heroicons/react/24/outline";
import { BillingStatusBadge } from "../BillingStatusBadge";
import type { BillingSummaryData } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { BillingSummaryData } from "@customer-portal/domain/billing";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
interface BillingSummaryProps extends React.HTMLAttributes<HTMLDivElement> {

View File

@ -8,8 +8,8 @@ import {
ServerIcon,
} from "@heroicons/react/24/outline";
import { format } from "date-fns";
import type { Invoice } from "@customer-portal/domain";
import { formatCurrency } from "@customer-portal/domain";
import type { Invoice } from "@customer-portal/domain/billing";
import { formatCurrency } from "@customer-portal/domain/billing";
const formatDate = (dateString?: string) => {
if (!dateString || dateString === "0000-00-00" || dateString === "0000-00-00 00:00:00")

View File

@ -2,8 +2,8 @@
import React from "react";
import Link from "next/link";
import { formatCurrency } from "@customer-portal/domain";
import type { InvoiceItem } from "@customer-portal/domain";
import { formatCurrency } from "@customer-portal/domain/billing";
import type { InvoiceItem } from "@customer-portal/domain/billing";
interface InvoiceItemsProps {
items?: InvoiceItem[];

View File

@ -1,8 +1,8 @@
import { useMemo } from "react";
import { format, formatDistanceToNowStrict } from "date-fns";
import { ArrowDownTrayIcon, ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { Invoice } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import type { Invoice } from "@customer-portal/domain/billing";
import { Button } from "@/components/atoms/button";
import { StatusPill } from "@/components/atoms/status-pill";
import { cn } from "@/lib/utils";

View File

@ -1,7 +1,7 @@
"use client";
import React from "react";
import { formatCurrency } from "@customer-portal/domain";
import { formatCurrency } from "@customer-portal/domain/billing";
interface InvoiceTotalsProps {
subtotal: number;

View File

@ -1,5 +1,5 @@
"use client";
import { formatCurrency } from "@customer-portal/domain";
import { formatCurrency } from "@customer-portal/domain/billing";
import { useRouter } from "next/navigation";
export function InvoiceItemRow({

View File

@ -9,7 +9,7 @@ import { PaginationBar } from "@/components/molecules/PaginationBar/PaginationBa
import { InvoiceTable } from "@/features/billing/components/InvoiceTable/InvoiceTable";
import { useInvoices } from "@/features/billing/hooks/useBilling";
import { useSubscriptionInvoices } from "@/features/subscriptions/hooks/useSubscriptions";
import type { Invoice } from "@customer-portal/domain";
import type { Invoice } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
interface InvoicesListProps {

View File

@ -16,8 +16,8 @@ import { CheckCircleIcon as CheckCircleIconSolid } from "@heroicons/react/24/sol
import { DataTable } from "@/components/molecules/DataTable/DataTable";
import { Button } from "@/components/atoms/button";
import { BillingStatusBadge } from "../BillingStatusBadge";
import type { Invoice } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { Invoice } from "@customer-portal/domain/billing";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
import { useCreateInvoiceSsoLink } from "@/features/billing/hooks/useBilling";
import { openSsoLink } from "@/features/billing/utils/sso";

View File

@ -1,7 +1,7 @@
"use client";
import { CreditCardIcon, BanknotesIcon, DevicePhoneMobileIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
import type { PaymentMethod } from "@customer-portal/domain";
import type { PaymentMethod } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
import type { ReactNode } from "react";

View File

@ -9,7 +9,7 @@ import {
ArrowPathIcon,
} from "@heroicons/react/24/outline";
import { Badge } from "@/components/atoms/badge";
import type { PaymentMethod } from "@customer-portal/domain";
import type { PaymentMethod } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
interface PaymentMethodCardProps extends React.HTMLAttributes<HTMLDivElement> {

View File

@ -16,12 +16,12 @@ import type {
InvoiceList,
InvoiceSsoLink,
PaymentMethodList,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import {
invoiceListSchema,
invoiceSchema as sharedInvoiceSchema,
} from "@customer-portal/domain/validation/shared/entities";
import { INVOICE_STATUS } from "@customer-portal/domain";
import { INVOICE_STATUS } from "@customer-portal/domain/billing";
const emptyInvoiceList: InvoiceList = {
invoices: [],

View File

@ -11,7 +11,7 @@ import { logger } from "@customer-portal/logging";
import { apiClient, getDataOrThrow } from "@/lib/api";
import { openSsoLink } from "@/features/billing/utils/sso";
import { useInvoice, useCreateInvoiceSsoLink } from "@/features/billing/hooks";
import type { InvoiceSsoLink } from "@customer-portal/domain";
import type { InvoiceSsoLink } from "@customer-portal/domain/billing";
import {
InvoiceItems,
InvoiceTotals,

View File

@ -1,7 +1,7 @@
"use client";
import { CheckCircleIcon } from "@heroicons/react/24/solid";
import type { CatalogProductBase } from "@customer-portal/domain";
import type { CatalogProductBase } from "@customer-portal/domain/billing";
import { getMonthlyPrice, getOneTimePrice } from "../../utils/pricing";
interface AddonGroupProps {

View File

@ -18,7 +18,7 @@ import {
} from "@heroicons/react/24/outline";
// Use canonical Address type from domain
import type { Address } from "@customer-portal/domain";
import type { Address } from "@customer-portal/domain/billing";
interface BillingInfo {
company: string | null;

View File

@ -3,7 +3,7 @@
import { useEffect } from "react";
import { MapPinIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { useZodForm } from "@customer-portal/validation";
import { addressFormSchema, type AddressFormData, type Address } from "@customer-portal/domain";
import { addressFormSchema, type AddressFormData, type Address } from "@customer-portal/domain/billing";
export interface AddressFormProps {
// Initial values

View File

@ -12,7 +12,7 @@ import { Button } from "@/components/atoms/button";
import { useRouter } from "next/navigation";
// Align with shared catalog contracts
import type { CatalogProductBase } from "@customer-portal/domain";
import type { CatalogProductBase } from "@customer-portal/domain/billing";
// Enhanced order item representation for UI summary
export type OrderItem = CatalogProductBase & {

View File

@ -1,5 +1,5 @@
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import type { CatalogProductBase } from "@customer-portal/domain";
import type { CatalogProductBase } from "@customer-portal/domain/billing";
import { useRouter } from "next/navigation";
import { Button } from "@/components/atoms/button";
import { getMonthlyPrice, getOneTimePrice } from "../../utils/pricing";

View File

@ -5,7 +5,7 @@ import { Skeleton } from "@/components/atoms/loading-skeleton";
import { Button } from "@/components/atoms/button";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { CreditCardIcon, CheckCircleIcon } from "@heroicons/react/24/outline";
import type { PaymentMethod } from "@customer-portal/domain";
import type { PaymentMethod } from "@customer-portal/domain/billing";
export interface PaymentFormProps {
existingMethods?: PaymentMethod[];

View File

@ -1,6 +1,6 @@
"use client";
import type { InternetInstallationCatalogItem } from "@customer-portal/domain";
import type { InternetInstallationCatalogItem } from "@customer-portal/domain/billing";
import { getDisplayPrice } from "../../utils/pricing";
import {
inferInstallationTypeFromSku,

View File

@ -5,7 +5,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
interface Props {
plan: InternetPlanCatalogItem | null;

View File

@ -6,7 +6,7 @@ import { CurrencyYenIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { useRouter } from "next/navigation";
import { getMonthlyPrice, getOneTimePrice } from "../../utils/pricing";

View File

@ -7,7 +7,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { ConfigureLoadingSkeleton } from "./components/ConfigureLoadingSkeleton";
import { ServiceConfigurationStep } from "./steps/ServiceConfigurationStep";
import { InstallationStep } from "./steps/InstallationStep";

View File

@ -5,7 +5,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type { AccessMode } from "../../../../hooks/useConfigureParams";
import { getMonthlyPrice, getOneTimePrice } from "../../../../utils/pricing";

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms";
import { AddonGroup } from "@/features/catalog/components/base/AddonGroup";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import type { InternetAddonCatalogItem } from "@customer-portal/domain";
import type { InternetAddonCatalogItem } from "@customer-portal/domain/billing";
interface Props {
addons: InternetAddonCatalogItem[];

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms";
import { InstallationOptions } from "../../InstallationOptions";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import type { InternetInstallationCatalogItem } from "@customer-portal/domain";
import type { InternetInstallationCatalogItem } from "@customer-portal/domain/billing";
interface Props {
installations: InternetInstallationCatalogItem[];

View File

@ -8,7 +8,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type { AccessMode } from "../../../../hooks/useConfigureParams";
import { getMonthlyPrice, getOneTimePrice } from "../../../../utils/pricing";

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { ArrowRightIcon } from "@heroicons/react/24/outline";
import type { InternetPlanCatalogItem } from "@customer-portal/domain";
import type { InternetPlanCatalogItem } from "@customer-portal/domain/billing";
import type { AccessMode } from "../../../../hooks/useConfigureParams";
interface Props {

View File

@ -3,7 +3,7 @@
import { DevicePhoneMobileIcon, UsersIcon, CurrencyYenIcon } from "@heroicons/react/24/outline";
import { AnimatedCard } from "@/components/molecules/AnimatedCard/AnimatedCard";
import { Button } from "@/components/atoms/button";
import type { SimCatalogProduct } from "@customer-portal/domain";
import type { SimCatalogProduct } from "@customer-portal/domain/billing";
import { getMonthlyPrice } from "../../utils/pricing";
interface SimPlanCardProps {

View File

@ -2,7 +2,7 @@
import React from "react";
import { UsersIcon } from "@heroicons/react/24/outline";
import type { SimCatalogProduct } from "@customer-portal/domain";
import type { SimCatalogProduct } from "@customer-portal/domain/billing";
import { SimPlanCard } from "./SimPlanCard";
interface SimPlanTypeSectionProps {

View File

@ -3,7 +3,7 @@
import { AnimatedCard } from "@/components/molecules";
import { Button } from "@/components/atoms/button";
import { CurrencyYenIcon } from "@heroicons/react/24/outline";
import type { VpnCatalogProduct } from "@customer-portal/domain";
import type { VpnCatalogProduct } from "@customer-portal/domain/billing";
interface VpnPlanCardProps {
plan: VpnCatalogProduct;

View File

@ -7,7 +7,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { inferInstallationTypeFromSku } from "../utils/inferInstallationType";
import { getMonthlyPrice, getOneTimePrice } from "../utils/pricing";

View File

@ -11,8 +11,8 @@ import {
type SimType,
type ActivationType,
type MnpData,
} from "@customer-portal/domain";
import type { SimCatalogProduct, SimActivationFeeCatalogItem } from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import type { SimCatalogProduct, SimActivationFeeCatalogItem } from "@customer-portal/domain/billing";
export type UseSimConfigureResult = {
// data

View File

@ -12,7 +12,7 @@ export * from "./hooks";
// Services
export * from "./services";
// Import domain types directly: import type { Address } from "@customer-portal/domain";
// Import domain types directly: import type { Address } from "@customer-portal/domain/billing";
// Utilities
export * from "./utils";

View File

@ -6,7 +6,7 @@ import type {
SimCatalogProduct,
SimActivationFeeCatalogItem,
VpnCatalogProduct,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
const emptyInternetPlans: InternetPlanCatalogItem[] = [];
const emptyInternetAddons: InternetAddonCatalogItem[] = [];

View File

@ -3,7 +3,7 @@
* Helper functions for catalog operations
*/
import { formatCurrency } from "@customer-portal/domain";
import { formatCurrency } from "@customer-portal/domain/billing";
import type {
CatalogFilter,
InternetPlanCatalogItem,
@ -11,7 +11,7 @@ import type {
InternetInstallationCatalogItem,
SimCatalogProduct,
VpnCatalogProduct,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
type CatalogProduct =
| InternetPlanCatalogItem

View File

@ -1,4 +1,4 @@
import type { CatalogProductBase } from "@customer-portal/domain";
import type { CatalogProductBase } from "@customer-portal/domain/billing";
export function getMonthlyPrice(product?: CatalogProductBase | null): number {
if (!product) return 0;

View File

@ -17,7 +17,7 @@ import type {
InternetPlanCatalogItem,
InternetInstallationCatalogItem,
InternetAddonCatalogItem,
} from "@customer-portal/domain";
} from "@customer-portal/domain/billing";
import { getMonthlyPrice } from "../utils/pricing";
import { LoadingCard, Skeleton, LoadingTable } from "@/components/atoms/loading-skeleton";
import { AnimatedCard } from "@/components/molecules";

View File

@ -15,7 +15,7 @@ import { LoadingCard, Skeleton } from "@/components/atoms/loading-skeleton";
import { Button } from "@/components/atoms/button";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { useSimCatalog } from "@/features/catalog/hooks";
import type { SimCatalogProduct } from "@customer-portal/domain";
import type { SimCatalogProduct } from "@customer-portal/domain/billing";
import { SimPlanTypeSection } from "@/features/catalog/components/sim/SimPlanTypeSection";
interface PlansByType {

View File

@ -7,13 +7,13 @@ import { ordersService } from "@/features/orders/services/orders.service";
import { usePaymentMethods } from "@/features/billing/hooks/useBilling";
import { usePaymentRefresh } from "@/features/billing/hooks/usePaymentRefresh";
import { getMonthlyPrice, getOneTimePrice } from "@/features/catalog/utils/pricing";
import type { CatalogProductBase } from "@customer-portal/domain";
import { createLoadingState, createSuccessState, createErrorState } from "@customer-portal/domain";
import type { AsyncState } from "@customer-portal/domain";
import type { CatalogProductBase } from "@customer-portal/domain/billing";
import { createLoadingState, createSuccessState, createErrorState } from "@customer-portal/domain/billing";
import type { AsyncState } from "@customer-portal/domain/billing";
import { useActiveSubscriptions } from "@/features/subscriptions/hooks/useSubscriptions";
// Use domain Address type
import type { Address } from "@customer-portal/domain";
import type { Address } from "@customer-portal/domain/billing";
type CheckoutItemType = "plan" | "installation" | "addon" | "activation" | "vpn";

View File

@ -8,7 +8,7 @@ import { PageAsync } from "@/components/molecules/AsyncBlock/AsyncBlock";
import { InlineToast } from "@/components/atoms/inline-toast";
import { StatusPill } from "@/components/atoms/status-pill";
import { AddressConfirmation } from "@/features/catalog/components/base/AddressConfirmation";
import { isLoading, isError, isSuccess } from "@customer-portal/domain";
import { isLoading, isError, isSuccess } from "@customer-portal/domain/billing";
import {
ExclamationTriangleIcon,
ShieldCheckIcon,

View File

@ -10,8 +10,8 @@ import {
getActivityNavigationPath,
isActivityClickable,
} from "../utils/dashboard.utils";
import type { Activity } from "@customer-portal/domain";
import type { ActivityFilter } from "@customer-portal/domain";
import type { Activity } from "@customer-portal/domain/billing";
import type { ActivityFilter } from "@customer-portal/domain/billing";
export interface ActivityFeedProps {
activities: Activity[];

View File

@ -3,7 +3,7 @@
import Link from "next/link";
import { CalendarDaysIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
import { format, formatDistanceToNow } from "date-fns";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
interface UpcomingPaymentBannerProps {
invoice: { id: number; amount: number; currency?: string; dueDate: string };

View File

@ -6,7 +6,7 @@
import { useQuery } from "@tanstack/react-query";
import { useAuthSession } from "@/features/auth/services/auth.store";
import { apiClient, queryKeys, getDataOrThrow } from "@/lib/api";
import type { DashboardSummary, DashboardError } from "@customer-portal/domain";
import type { DashboardSummary, DashboardError } from "@customer-portal/domain/billing";
class DashboardDataError extends Error {
constructor(

View File

@ -3,8 +3,8 @@
* Helper functions for dashboard data processing and formatting
*/
import type { Activity } from "@customer-portal/domain";
import type { ActivityFilter, ActivityFilterConfig } from "@customer-portal/domain";
import type { Activity } from "@customer-portal/domain/billing";
import type { ActivityFilter, ActivityFilterConfig } from "@customer-portal/domain/billing";
/**
* Activity filter configurations

View File

@ -3,7 +3,7 @@
import { useState, useEffect } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import type { Activity, DashboardSummary } from "@customer-portal/domain";
import type { Activity, DashboardSummary } from "@customer-portal/domain/billing";
import {
ServerIcon,
ChatBubbleLeftRightIcon,
@ -25,7 +25,7 @@ import { useDashboardSummary } from "@/features/dashboard/hooks";
import { StatCard, QuickAction, DashboardActivityItem } from "@/features/dashboard/components";
import { LoadingStats, LoadingTable } from "@/components/atoms";
import { ErrorState } from "@/components/atoms/error-state";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import { log } from "@customer-portal/logging";
import { useCreateInvoiceSsoLink } from "@/features/billing/hooks/useBilling";

View File

@ -1,5 +1,5 @@
import { apiClient } from "@/lib/api";
import type { CreateOrderRequest } from "@customer-portal/domain";
import type { CreateOrderRequest } from "@customer-portal/domain/billing";
async function createOrder<T = { sfOrderId: string }>(payload: CreateOrderRequest): Promise<T> {
const response = await apiClient.POST("/api/orders", { body: payload });

View File

@ -11,7 +11,7 @@ import {
ExclamationTriangleIcon,
XCircleIcon,
} from "@heroicons/react/24/outline";
import type { SimDetails } from "@customer-portal/contracts/sim";
import type { SimDetails } from "@customer-portal/domain/sim";
interface SimDetailsCardProps {
simDetails: SimDetails;

View File

@ -15,8 +15,8 @@ import {
import { StatusPill } from "@/components/atoms/status-pill";
import { Button } from "@/components/atoms/button";
import { SubCard } from "@/components/molecules/SubCard/SubCard";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { Subscription } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import type { Subscription } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
interface SubscriptionCardProps {

View File

@ -15,8 +15,8 @@ import {
} from "@heroicons/react/24/outline";
import { StatusPill } from "@/components/atoms/status-pill";
import { SubCard } from "@/components/molecules/SubCard/SubCard";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { Subscription } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import type { Subscription } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils";
interface SubscriptionDetailsProps {

View File

@ -6,7 +6,7 @@
import { useQuery } from "@tanstack/react-query";
import { apiClient, queryKeys, getDataOrDefault, getDataOrThrow, getNullableData } from "@/lib/api";
import { useAuthSession } from "@/features/auth/services";
import type { InvoiceList, Subscription, SubscriptionList } from "@customer-portal/domain";
import type { InvoiceList, Subscription, SubscriptionList } from "@customer-portal/domain/billing";
interface UseSubscriptionsOptions {
status?: string;

View File

@ -20,7 +20,7 @@ import {
import { format } from "date-fns";
import { useSubscription } from "@/features/subscriptions/hooks";
import { InvoicesList } from "@/features/billing/components/InvoiceList/InvoiceList";
import { formatCurrency as sharedFormatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import { formatCurrency as sharedFormatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import { SimManagementSection } from "@/features/sim-management";
export function SubscriptionDetailContainer() {

View File

@ -23,8 +23,8 @@ import {
} from "@heroicons/react/24/outline";
import { format } from "date-fns";
import { useSubscriptions, useSubscriptionStats } from "@/features/subscriptions/hooks";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain";
import type { Subscription } from "@customer-portal/domain";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import type { Subscription } from "@customer-portal/domain/billing";
export function SubscriptionsListContainer() {
const router = useRouter();

View File

@ -91,12 +91,4 @@ export const authResponseSchema = z.object({
tokens: authTokensSchema,
});
export const validateSignupRequestSchema = signupRequestSchema.pick({ sfNumber: true });
export const accountStatusRequestSchema = z.object({ email: emailSchema });
export const ssoLinkRequestSchema = z.object({ destination: z.string().optional() });
export const checkPasswordNeededRequestSchema = z.object({ email: emailSchema });
export const refreshTokenRequestSchema = z.object({
refreshToken: z.string().min(1, "Refresh token is required").optional(),
deviceId: z.string().optional(),
});

View File

@ -39,6 +39,14 @@ export type CancelPlanRequest = Requests.FreebitCancelPlanRequest;
export type CancelPlanApiRequest = Requests.FreebitCancelPlanApiRequest;
export type CancelAccountRequest = Requests.FreebitCancelAccountRequest;
export type AuthRequest = Requests.FreebitAuthRequest;
export type TopUpResponse = ReturnType<typeof Mapper.transformFreebitTopUpResponse>;
export type AddSpecResponse = ReturnType<typeof Mapper.transformFreebitAddSpecResponse>;
export type PlanChangeResponse = ReturnType<typeof Mapper.transformFreebitPlanChangeResponse>;
export type CancelPlanResponse = ReturnType<typeof Mapper.transformFreebitCancelPlanResponse>;
export type CancelAccountResponse = ReturnType<typeof Mapper.transformFreebitCancelAccountResponse>;
export type EsimReissueResponse = ReturnType<typeof Mapper.transformFreebitEsimReissueResponse>;
export type EsimAddAccountResponse = ReturnType<typeof Mapper.transformFreebitEsimAddAccountResponse>;
export type EsimActivationResponse = ReturnType<typeof Mapper.transformFreebitEsimActivationResponse>;
export * from "./mapper";
export * from "./raw.types";

View File

@ -125,16 +125,12 @@ export function transformFreebitTrafficInfo(raw: unknown): SimUsage {
const simUsage: SimUsage = {
account: asString(response.account),
todayUsageMb: asNumber(response.todayData) / 1024,
todayUsageKb: asNumber(response.todayData),
monthlyUsageMb: response.thisMonthData ? asNumber(response.thisMonthData) / 1024 : undefined,
monthlyUsageKb: response.thisMonthData ? asNumber(response.thisMonthData) : undefined,
recentDaysUsage: (response.daily || []).map(day => ({
date: day.usageDate || "",
usageKb: asNumber(day.trafficKb),
usageMb: asNumber(day.trafficKb) / 1024,
})),
isBlacklisted: parseBooleanFlag(response.blacklistFlg),
todayUsageMb: response.traffic?.today ? asNumber(response.traffic.today) / 1024 : 0,
todayUsageKb: response.traffic?.today ? asNumber(response.traffic.today) : 0,
monthlyUsageMb: undefined,
monthlyUsageKb: undefined,
recentDaysUsage: [],
isBlacklisted: parseBooleanFlag(response.traffic?.blackList),
lastUpdated: new Date().toISOString(),
};
@ -146,9 +142,9 @@ export function transformFreebitQuotaHistory(raw: unknown): SimTopUpHistory {
const history: SimTopUpHistory = {
account: asString(response.account),
totalAdditions: asNumber(response.totalAddQuotaKb),
additionCount: asNumber(response.addQuotaCount),
history: (response.details || []).map(detail => ({
totalAdditions: asNumber(response.total),
additionCount: asNumber(response.count),
history: (response.quotaHistory || []).map(detail => ({
quotaKb: asNumber(detail.addQuotaKb),
quotaMb: asNumber(detail.addQuotaKb) / 1024,
addedDate: detail.addDate || "",
@ -168,27 +164,46 @@ export function transformFreebitTopUpResponse(raw: unknown) {
return freebitTopUpRawSchema.parse(raw);
}
export type FreebitTopUpResponse = ReturnType<typeof transformFreebitTopUpResponse>;
export function transformFreebitAddSpecResponse(raw: unknown) {
return freebitAddSpecRawSchema.parse(raw);
}
export type FreebitAddSpecResponse = ReturnType<typeof transformFreebitAddSpecResponse>;
export function transformFreebitPlanChangeResponse(raw: unknown) {
return freebitPlanChangeRawSchema.parse(raw);
}
export type FreebitPlanChangeResponse = ReturnType<typeof transformFreebitPlanChangeResponse>;
export function transformFreebitCancelPlanResponse(raw: unknown) {
return freebitCancelPlanRawSchema.parse(raw);
}
export type FreebitCancelPlanResponse = ReturnType<typeof transformFreebitCancelPlanResponse>;
export function transformFreebitCancelAccountResponse(raw: unknown) {
return freebitCancelAccountRawSchema.parse(raw);
}
export type FreebitCancelAccountResponse = ReturnType<typeof transformFreebitCancelAccountResponse>;
export function transformFreebitEsimReissueResponse(raw: unknown) {
return freebitEsimReissueRawSchema.parse(raw);
}
export type FreebitEsimReissueResponse = ReturnType<typeof transformFreebitEsimReissueResponse>;
export function transformFreebitEsimAddAccountResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}
export type FreebitEsimAddAccountResponse = ReturnType<typeof transformFreebitEsimAddAccountResponse>;
export function transformFreebitEsimActivationResponse(raw: unknown) {
return freebitEsimAddAccountRawSchema.parse(raw);
}

View File

@ -145,11 +145,16 @@ export type FreebitTrafficInfoRaw = z.infer<typeof freebitTrafficInfoRawSchema>;
// Freebit Quota History Response
export const freebitQuotaHistoryRawSchema = z.object({
resultCode: z.string().optional(),
resultMessage: z.string().optional(),
status: z
.object({
message: z.string().optional(),
statusCode: z.union([z.string(), z.number()]).optional(),
})
.optional(),
account: z.union([z.string(), z.number()]).optional(),
totalAddQuotaKb: z.union([z.string(), z.number()]).optional(),
addQuotaCount: z.union([z.string(), z.number()]).optional(),
details: z.array(
total: z.union([z.string(), z.number()]).optional(),
count: z.union([z.string(), z.number()]).optional(),
quotaHistory: z.array(
z.object({
addQuotaKb: z.union([z.string(), z.number()]).optional(),
addDate: z.string().optional(),

View File

@ -90,6 +90,37 @@ export const freebitCancelPlanApiRequestSchema = z.object({
runTime: z.string().optional(),
});
export const freebitQuotaHistoryRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
fromDate: z.string().regex(/^\d{8}$/, "From date must be in YYYYMMDD format"),
toDate: z.string().regex(/^\d{8}$/, "To date must be in YYYYMMDD format"),
});
export const freebitQuotaHistoryResponseSchema = z.object({
resultCode: z.string(),
status: z
.object({
message: z.string(),
statusCode: z.union([z.string(), z.number()]),
})
.optional(),
total: z.union([z.string(), z.number()]),
count: z.union([z.string(), z.number()]),
quotaHistory: z.array(
z.object({
addQuotaKb: z.union([z.string(), z.number()]),
addDate: z.string(),
expireDate: z.string(),
campaignCode: z.string().optional(),
})
),
});
export const freebitEsimMnpSchema = z.object({
reserveNumber: z.string().min(1, "Reserve number is required"),
reserveExpireDate: z.string().regex(/^\d{8}$/, "Reserve expire date must be in YYYYMMDD format"),
});
export const freebitEsimReissueRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
newEid: z.string().min(1, "New EID is required"),
@ -98,9 +129,21 @@ export const freebitEsimReissueRequestSchema = z.object({
oldProductNumber: z.string().optional(),
});
// ============================================================================
export const freebitEsimAddAccountRequestSchema = z.object({
authKey: z.string().min(1).optional(),
aladinOperated: z.enum(["10", "20"]).default("10"),
account: z.string().min(1, "Account is required"),
eid: z.string().min(1, "EID is required"),
addKind: z.enum(["N", "R"]).default("N"),
shipDate: z.string().regex(/^\d{8}$/, "Ship date must be in YYYYMMDD format").optional(),
planCode: z.string().optional(),
contractLine: z.enum(["4G", "5G"]).optional(),
mnp: freebitEsimMnpSchema.optional(),
});
// =========================================================================
// SIM Features
// ============================================================================
// =========================================================================
export const freebitSimFeaturesRequestSchema = z.object({
account: z.string().min(1, "Account is required"),
@ -115,14 +158,9 @@ export const freebitGlobalIpRequestSchema = z.object({
assign: z.boolean(), // true to assign, false to remove
});
// ============================================================================
// =========================================================================
// eSIM Activation
// ============================================================================
export const freebitEsimMnpSchema = z.object({
reserveNumber: z.string().min(1, "Reserve number is required"),
reserveExpireDate: z.string().regex(/^\d{8}$/, "Reserve expire date must be in YYYYMMDD format"),
});
// =========================================================================
export const freebitAuthRequestSchema = z.object({
oemId: z.string().min(1),

View File

@ -1,116 +0,0 @@
export type UserId = string & {
readonly __brand: "UserId";
};
export type OrderId = string & {
readonly __brand: "OrderId";
};
export type InvoiceId = string & {
readonly __brand: "InvoiceId";
};
export type SubscriptionId = string & {
readonly __brand: "SubscriptionId";
};
export type PaymentId = string & {
readonly __brand: "PaymentId";
};
export type CaseId = string & {
readonly __brand: "CaseId";
};
export type SessionId = string & {
readonly __brand: "SessionId";
};
export type WhmcsClientId = number & {
readonly __brand: "WhmcsClientId";
};
export type WhmcsInvoiceId = number & {
readonly __brand: "WhmcsInvoiceId";
};
export type WhmcsProductId = number & {
readonly __brand: "WhmcsProductId";
};
export type SalesforceContactId = string & {
readonly __brand: "SalesforceContactId";
};
export type SalesforceAccountId = string & {
readonly __brand: "SalesforceAccountId";
};
export type SalesforceCaseId = string & {
readonly __brand: "SalesforceCaseId";
};
export declare const createUserId: (id: string) => UserId;
export declare const createOrderId: (id: string) => OrderId;
export declare const createInvoiceId: (id: string) => InvoiceId;
export declare const createSubscriptionId: (id: string) => SubscriptionId;
export declare const createPaymentId: (id: string) => PaymentId;
export declare const createCaseId: (id: string) => CaseId;
export declare const createSessionId: (id: string) => SessionId;
export declare const createWhmcsClientId: (id: number) => WhmcsClientId;
export declare const createWhmcsInvoiceId: (id: number) => WhmcsInvoiceId;
export declare const createWhmcsProductId: (id: number) => WhmcsProductId;
export declare const createSalesforceContactId: (id: string) => SalesforceContactId;
export declare const createSalesforceAccountId: (id: string) => SalesforceAccountId;
export declare const createSalesforceCaseId: (id: string) => SalesforceCaseId;
export declare const isUserId: (id: string) => id is UserId;
export declare const isOrderId: (id: string) => id is OrderId;
export declare const isInvoiceId: (id: string) => id is InvoiceId;
export declare const isWhmcsClientId: (id: number) => id is WhmcsClientId;
export type IsoDateTimeString = string;
export interface BaseEntity {
id: string;
createdAt: string;
updatedAt: string;
}
export interface WhmcsEntity {
id: number;
}
export interface SalesforceEntity {
id: string;
createdDate: string;
lastModifiedDate: string;
}
export interface Paginated<T> {
items: T[];
nextCursor: string | null;
totalCount?: number;
}
export interface IdempotencyKey {
key: string;
userId: string;
createdAt: string;
}
export interface UserMapping {
userId: string;
whmcsClientId: number;
sfContactId?: string;
sfAccountId?: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserIdMapping extends UserMapping {
createdAt?: Date;
updatedAt?: Date;
}
export interface CreateMappingRequest {
userId: string;
whmcsClientId: number;
sfAccountId?: string;
}
export interface UpdateMappingRequest {
whmcsClientId?: number;
sfAccountId?: string;
}
export interface MappingStats {
totalMappings: number;
whmcsMappings: number;
salesforceMappings: number;
completeMappings: number;
orphanedMappings: number;
}
export interface Address {
street: string | null;
streetLine2: string | null;
city: string | null;
state: string | null;
postalCode: string | null;
country: string | null;
}

View File

@ -1,38 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isWhmcsClientId = exports.isInvoiceId = exports.isOrderId = exports.isUserId = exports.createSalesforceCaseId = exports.createSalesforceAccountId = exports.createSalesforceContactId = exports.createWhmcsProductId = exports.createWhmcsInvoiceId = exports.createWhmcsClientId = exports.createSessionId = exports.createCaseId = exports.createPaymentId = exports.createSubscriptionId = exports.createInvoiceId = exports.createOrderId = exports.createUserId = void 0;
const createUserId = (id) => id;
exports.createUserId = createUserId;
const createOrderId = (id) => id;
exports.createOrderId = createOrderId;
const createInvoiceId = (id) => id;
exports.createInvoiceId = createInvoiceId;
const createSubscriptionId = (id) => id;
exports.createSubscriptionId = createSubscriptionId;
const createPaymentId = (id) => id;
exports.createPaymentId = createPaymentId;
const createCaseId = (id) => id;
exports.createCaseId = createCaseId;
const createSessionId = (id) => id;
exports.createSessionId = createSessionId;
const createWhmcsClientId = (id) => id;
exports.createWhmcsClientId = createWhmcsClientId;
const createWhmcsInvoiceId = (id) => id;
exports.createWhmcsInvoiceId = createWhmcsInvoiceId;
const createWhmcsProductId = (id) => id;
exports.createWhmcsProductId = createWhmcsProductId;
const createSalesforceContactId = (id) => id;
exports.createSalesforceContactId = createSalesforceContactId;
const createSalesforceAccountId = (id) => id;
exports.createSalesforceAccountId = createSalesforceAccountId;
const createSalesforceCaseId = (id) => id;
exports.createSalesforceCaseId = createSalesforceCaseId;
const isUserId = (id) => typeof id === "string";
exports.isUserId = isUserId;
const isOrderId = (id) => typeof id === "string";
exports.isOrderId = isOrderId;
const isInvoiceId = (id) => typeof id === "string";
exports.isInvoiceId = isInvoiceId;
const isWhmcsClientId = (id) => typeof id === "number";
exports.isWhmcsClientId = isWhmcsClientId;
//# sourceMappingURL=common.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"common.js","sourceRoot":"","sources":["common.ts"],"names":[],"mappings":";;;AA0BO,MAAM,YAAY,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAY,CAAC;AAApD,QAAA,YAAY,gBAAwC;AAC1D,MAAM,aAAa,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,EAAa,CAAC;AAAvD,QAAA,aAAa,iBAA0C;AAC7D,MAAM,eAAe,GAAG,CAAC,EAAU,EAAa,EAAE,CAAC,EAAe,CAAC;AAA7D,QAAA,eAAe,mBAA8C;AACnE,MAAM,oBAAoB,GAAG,CAAC,EAAU,EAAkB,EAAE,CAAC,EAAoB,CAAC;AAA5E,QAAA,oBAAoB,wBAAwD;AAClF,MAAM,eAAe,GAAG,CAAC,EAAU,EAAa,EAAE,CAAC,EAAe,CAAC;AAA7D,QAAA,eAAe,mBAA8C;AACnE,MAAM,YAAY,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,EAAY,CAAC;AAApD,QAAA,YAAY,gBAAwC;AAC1D,MAAM,eAAe,GAAG,CAAC,EAAU,EAAa,EAAE,CAAC,EAAe,CAAC;AAA7D,QAAA,eAAe,mBAA8C;AAEnE,MAAM,mBAAmB,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,EAAmB,CAAC;AAAzE,QAAA,mBAAmB,uBAAsD;AAC/E,MAAM,oBAAoB,GAAG,CAAC,EAAU,EAAkB,EAAE,CAAC,EAAoB,CAAC;AAA5E,QAAA,oBAAoB,wBAAwD;AAClF,MAAM,oBAAoB,GAAG,CAAC,EAAU,EAAkB,EAAE,CAAC,EAAoB,CAAC;AAA5E,QAAA,oBAAoB,wBAAwD;AAElF,MAAM,yBAAyB,GAAG,CAAC,EAAU,EAAuB,EAAE,CAC3E,EAAyB,CAAC;AADf,QAAA,yBAAyB,6BACV;AACrB,MAAM,yBAAyB,GAAG,CAAC,EAAU,EAAuB,EAAE,CAC3E,EAAyB,CAAC;AADf,QAAA,yBAAyB,6BACV;AACrB,MAAM,sBAAsB,GAAG,CAAC,EAAU,EAAoB,EAAE,CAAC,EAAsB,CAAC;AAAlF,QAAA,sBAAsB,0BAA4D;AAGxF,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC;AAAhE,QAAA,QAAQ,YAAwD;AACtE,MAAM,SAAS,GAAG,CAAC,EAAU,EAAiB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC;AAAlE,QAAA,SAAS,aAAyD;AACxE,MAAM,WAAW,GAAG,CAAC,EAAU,EAAmB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC;AAAtE,QAAA,WAAW,eAA2D;AAC5E,MAAM,eAAe,GAAG,CAAC,EAAU,EAAuB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC;AAA9E,QAAA,eAAe,mBAA+D"}

View File

@ -1,135 +0,0 @@
// Common types used across the application
// =====================================================
// BRANDED TYPES FOR TYPE SAFETY
// =====================================================
// Branded types for critical identifiers
export type UserId = string & { readonly __brand: "UserId" };
export type OrderId = string & { readonly __brand: "OrderId" };
export type InvoiceId = string & { readonly __brand: "InvoiceId" };
export type SubscriptionId = string & { readonly __brand: "SubscriptionId" };
export type PaymentId = string & { readonly __brand: "PaymentId" };
export type CaseId = string & { readonly __brand: "CaseId" };
export type SessionId = string & { readonly __brand: "SessionId" };
// WHMCS-specific branded types
export type WhmcsClientId = number & { readonly __brand: "WhmcsClientId" };
export type WhmcsInvoiceId = number & { readonly __brand: "WhmcsInvoiceId" };
export type WhmcsProductId = number & { readonly __brand: "WhmcsProductId" };
// Salesforce-specific branded types
export type SalesforceContactId = string & { readonly __brand: "SalesforceContactId" };
export type SalesforceAccountId = string & { readonly __brand: "SalesforceAccountId" };
export type SalesforceCaseId = string & { readonly __brand: "SalesforceCaseId" };
// Helper functions for creating branded types
export const createUserId = (id: string): UserId => id as UserId;
export const createOrderId = (id: string): OrderId => id as OrderId;
export const createInvoiceId = (id: string): InvoiceId => id as InvoiceId;
export const createSubscriptionId = (id: string): SubscriptionId => id as SubscriptionId;
export const createPaymentId = (id: string): PaymentId => id as PaymentId;
export const createCaseId = (id: string): CaseId => id as CaseId;
export const createSessionId = (id: string): SessionId => id as SessionId;
export const createWhmcsClientId = (id: number): WhmcsClientId => id as WhmcsClientId;
export const createWhmcsInvoiceId = (id: number): WhmcsInvoiceId => id as WhmcsInvoiceId;
export const createWhmcsProductId = (id: number): WhmcsProductId => id as WhmcsProductId;
export const createSalesforceContactId = (id: string): SalesforceContactId =>
id as SalesforceContactId;
export const createSalesforceAccountId = (id: string): SalesforceAccountId =>
id as SalesforceAccountId;
export const createSalesforceCaseId = (id: string): SalesforceCaseId => id as SalesforceCaseId;
// Type guards for branded types
export const isUserId = (id: string): id is UserId => typeof id === "string";
export const isOrderId = (id: string): id is OrderId => typeof id === "string";
export const isInvoiceId = (id: string): id is InvoiceId => typeof id === "string";
export const isWhmcsClientId = (id: number): id is WhmcsClientId => typeof id === "number";
// Shared ISO8601 timestamp string type used for serialized dates
export type IsoDateTimeString = string;
// =====================================================
// BASE ENTITY INTERFACES
// =====================================================
// Base entity interfaces for different systems
// Portal entities (User, etc.)
export interface BaseEntity {
id: string;
createdAt: string;
updatedAt: string;
}
// WHMCS entities (Invoice, Subscription, etc.)
export interface WhmcsEntity {
id: number;
}
// Salesforce entities (SupportCase, etc.)
export interface SalesforceEntity {
id: string;
createdDate: string;
lastModifiedDate: string;
}
export interface Paginated<T> {
items: T[];
nextCursor: string | null;
totalCount?: number;
}
// API types moved to contracts/api.ts
export interface IdempotencyKey {
key: string;
userId: string;
createdAt: string;
}
export interface UserMapping {
userId: string;
whmcsClientId: number;
sfContactId?: string;
sfAccountId?: string;
createdAt?: Date;
updatedAt?: Date;
}
// Extended mapping interfaces for V2 implementation
export interface UserIdMapping extends UserMapping {
createdAt?: Date;
updatedAt?: Date;
}
export interface CreateMappingRequest {
userId: string;
whmcsClientId: number;
sfAccountId?: string;
}
export interface UpdateMappingRequest {
whmcsClientId?: number;
sfAccountId?: string;
}
export interface MappingStats {
totalMappings: number;
whmcsMappings: number;
salesforceMappings: number;
completeMappings: number;
orphanedMappings: number;
}
// Shared address type used across BFF and Portal
export interface Address {
street: string | null;
streetLine2: string | null;
city: string | null;
state: string | null;
postalCode: string | null;
country: string | null;
}

View File

@ -1,75 +0,0 @@
export type ApiResponse<T = unknown> = ApiSuccess<T> | ApiFailure;
export interface ApiSuccess<T = unknown> {
success: true;
data: T;
meta?: ApiMeta;
}
export interface ApiFailure {
success: false;
error: ApiError;
meta?: ApiMeta;
}
export interface ApiError {
code: string;
message: string;
details?: Record<string, unknown>;
statusCode?: number;
timestamp?: string;
path?: string;
}
export interface ApiMeta {
requestId?: string;
timestamp?: string;
version?: string;
}
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
};
}
export interface QueryParams extends Record<string, unknown> {
page?: number;
limit?: number;
search?: string;
filter?: Record<string, unknown>;
sort?: string;
}
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
export interface ApiRequestConfig {
method?: HttpMethod;
headers?: Record<string, string>;
params?: QueryParams;
data?: unknown;
timeout?: number;
retries?: number;
cache?: boolean;
}
export interface ApiClient {
get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
post<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
put<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
patch<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
}
export interface ApiClientError {
code?: string;
message: string;
details?: Record<string, unknown>;
statusCode?: number;
timestamp?: string;
}
export type RequestInterceptor = (config: ApiRequestConfig) => ApiRequestConfig | Promise<ApiRequestConfig>;
export type ResponseInterceptor = <T>(response: ApiResponse<T>) => ApiResponse<T> | Promise<ApiResponse<T>>;
export interface CrudService<T, CreateT = Partial<T>, UpdateT = Partial<T>> {
getAll(params?: QueryParams): Promise<PaginatedResponse<T>>;
getById(id: string): Promise<T>;
create(data: CreateT): Promise<T>;
update(id: string, data: UpdateT): Promise<T>;
delete(id: string): Promise<void>;
}

View File

@ -1,3 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=api.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"api.js","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":""}

View File

@ -1,101 +0,0 @@
// API response envelope types
export type ApiResponse<T = unknown> = ApiSuccess<T> | ApiFailure;
export interface ApiSuccess<T = unknown> {
success: true;
data: T;
meta?: ApiMeta;
}
export interface ApiFailure {
success: false;
error: ApiError;
meta?: ApiMeta;
}
export interface ApiError {
code: string;
message: string;
details?: Record<string, unknown>;
statusCode?: number;
timestamp?: string;
path?: string;
}
export interface ApiMeta {
requestId?: string;
timestamp?: string;
version?: string;
}
// Generic pagination interface (business contract)
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
};
}
// Query parameters for API requests (business contract)
export interface QueryParams extends Record<string, unknown> {
page?: number;
limit?: number;
search?: string;
filter?: Record<string, unknown>;
sort?: string;
}
// HTTP methods (technical contract)
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
// API request configuration (technical contract)
export interface ApiRequestConfig {
method?: HttpMethod;
headers?: Record<string, string>;
params?: QueryParams;
data?: unknown;
timeout?: number;
retries?: number;
cache?: boolean;
}
// API client interface (technical contract)
export interface ApiClient {
get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
post<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
put<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
patch<T>(url: string, data?: unknown, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T>>;
}
// API client error interface (technical contract)
export interface ApiClientError {
code?: string;
message: string;
details?: Record<string, unknown>;
statusCode?: number;
timestamp?: string;
}
// Interceptor types (technical contract)
export type RequestInterceptor = (
config: ApiRequestConfig
) => ApiRequestConfig | Promise<ApiRequestConfig>;
export type ResponseInterceptor = <T>(
response: ApiResponse<T>
) => ApiResponse<T> | Promise<ApiResponse<T>>;
// Generic CRUD service interface (business contract)
export interface CrudService<T, CreateT = Partial<T>, UpdateT = Partial<T>> {
getAll(params?: QueryParams): Promise<PaginatedResponse<T>>;
getById(id: string): Promise<T>;
create(data: CreateT): Promise<T>;
update(id: string, data: UpdateT): Promise<T>;
delete(id: string): Promise<void>;
}

View File

@ -1,60 +0,0 @@
export interface CatalogProductBase {
id: string;
sku: string;
name: string;
description?: string;
displayOrder?: number;
billingCycle?: string;
monthlyPrice?: number;
oneTimePrice?: number;
unitPrice?: number;
}
export interface InternetCatalogProduct extends CatalogProductBase {
internetPlanTier?: string;
internetOfferingType?: string;
features?: string[];
}
export interface InternetPlanTemplate {
tierDescription: string;
description?: string;
features?: string[];
}
export interface InternetPlanCatalogItem extends InternetCatalogProduct {
catalogMetadata?: {
tierDescription?: string;
features?: string[];
isRecommended?: boolean;
};
}
export interface InternetInstallationCatalogItem extends InternetCatalogProduct {
catalogMetadata?: {
installationTerm: "One-time" | "12-Month" | "24-Month";
};
}
export interface InternetAddonCatalogItem extends InternetCatalogProduct {
isBundledAddon?: boolean;
bundledAddonId?: string;
}
export interface SimCatalogProduct extends CatalogProductBase {
simDataSize?: string;
simPlanType?: string;
simHasFamilyDiscount?: boolean;
isBundledAddon?: boolean;
bundledAddonId?: string;
}
export interface SimActivationFeeCatalogItem extends SimCatalogProduct {
catalogMetadata?: {
isDefault: boolean;
};
}
export interface VpnCatalogProduct extends CatalogProductBase {
vpnRegion?: string;
}
export interface CatalogPricebookEntry {
id?: string;
name?: string;
unitPrice?: number;
pricebook2Id?: string;
product2Id?: string;
isActive?: boolean;
}

View File

@ -1,3 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=catalog.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["catalog.ts"],"names":[],"mappings":""}

View File

@ -1,70 +0,0 @@
// Shared catalog contracts consumed by both BFF and Portal
export interface CatalogProductBase {
id: string;
sku: string;
name: string;
description?: string;
displayOrder?: number;
billingCycle?: string;
monthlyPrice?: number;
oneTimePrice?: number;
unitPrice?: number;
}
export interface InternetCatalogProduct extends CatalogProductBase {
internetPlanTier?: string;
internetOfferingType?: string;
features?: string[];
}
export interface InternetPlanTemplate {
tierDescription: string;
description?: string;
features?: string[];
}
export interface InternetPlanCatalogItem extends InternetCatalogProduct {
catalogMetadata?: {
tierDescription?: string;
features?: string[];
isRecommended?: boolean;
};
}
export interface InternetInstallationCatalogItem extends InternetCatalogProduct {
catalogMetadata?: {
installationTerm: "One-time" | "12-Month" | "24-Month";
};
}
export interface InternetAddonCatalogItem extends InternetCatalogProduct {
isBundledAddon?: boolean;
bundledAddonId?: string;
}
export interface SimCatalogProduct extends CatalogProductBase {
simDataSize?: string;
simPlanType?: string;
simHasFamilyDiscount?: boolean;
isBundledAddon?: boolean;
bundledAddonId?: string;
}
export interface SimActivationFeeCatalogItem extends SimCatalogProduct {
catalogMetadata?: {
isDefault: boolean;
};
}
export interface VpnCatalogProduct extends CatalogProductBase {
vpnRegion?: string;
}
export interface CatalogPricebookEntry {
id?: string;
name?: string;
unitPrice?: number;
pricebook2Id?: string;
product2Id?: string;
isActive?: boolean;
}

View File

@ -1,3 +0,0 @@
export * from "./api";
export * from "./catalog";
export * from "./salesforce";

View File

@ -1,20 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./api"), exports);
__exportStar(require("./catalog"), exports);
__exportStar(require("./salesforce"), exports);
//# sourceMappingURL=index.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,wCAAsB;AACtB,4CAA0B;AAC1B,+CAA6B"}

Some files were not shown because too many files have changed in this diff Show More