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 { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";

View File

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

View File

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

View File

@ -9,7 +9,7 @@ import {
type AccountData, type AccountData,
type UpsertResult, type UpsertResult,
} from "./services/salesforce-account.service"; } 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 * 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 { Logger } from "nestjs-pino";
import { getErrorMessage } from "@bff/core/utils/error.util"; import { getErrorMessage } from "@bff/core/utils/error.util";
import { SalesforceConnection } from "./salesforce-connection.service"; 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 { export interface AccountData {
name: string; name: string;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import {
Inject, Inject,
} from "@nestjs/common"; } from "@nestjs/common";
import { Logger } from "nestjs-pino"; 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 { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service"; import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { getErrorMessage } from "@bff/core/utils/error.util"; import { getErrorMessage } from "@bff/core/utils/error.util";

View File

@ -1,6 +1,6 @@
import { Injectable, Inject } from "@nestjs/common"; import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino"; 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 { InvoiceRetrievalService } from "./invoice-retrieval.service";
import { InvoiceHealthService } from "./invoice-health.service"; import { InvoiceHealthService } from "./invoice-health.service";
import { InvoiceValidatorService } from "../validators/invoice-validator.service"; import { InvoiceValidatorService } from "../validators/invoice-validator.service";

View File

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

View File

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

View File

@ -3,7 +3,7 @@
import { SubCard } from "@/components/molecules/SubCard/SubCard"; import { SubCard } from "@/components/molecules/SubCard/SubCard";
import { MapPinIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline"; import { MapPinIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { AddressForm, type AddressFormProps } from "@/features/catalog/components"; 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 { interface AddressCardProps {
address: Address; address: Address;

View File

@ -2,7 +2,7 @@
import { SubCard } from "@/components/molecules/SubCard/SubCard"; import { SubCard } from "@/components/molecules/SubCard/SubCard";
import { UserIcon, PencilIcon, CheckIcon, XMarkIcon } from "@heroicons/react/24/outline"; 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 { interface PersonalInfoCardProps {
data: ProfileDisplayData; data: ProfileDisplayData;

View File

@ -6,7 +6,7 @@ import {
addressFormSchema, addressFormSchema,
addressFormToRequest, addressFormToRequest,
type AddressFormData, type AddressFormData,
} from "@customer-portal/domain"; } from "@customer-portal/domain/billing";
import { useZodForm } from "@customer-portal/validation"; import { useZodForm } from "@customer-portal/validation";
export function useAddressEdit(initial: AddressFormData) { 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"; import { logger } from "@customer-portal/logging";
// Use centralized profile types // 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 // Address type moved to domain package
import type { Address } from "@customer-portal/domain"; import type { Address } from "@customer-portal/domain/billing";
export function useProfileData() { export function useProfileData() {
const { user } = useAuthStore(); const { user } = useAuthStore();

View File

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

View File

@ -1,5 +1,5 @@
import { apiClient, getDataOrThrow, getNullableData } from "@/lib/api"; 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 = { type ProfileUpdateInput = {
firstName?: string; firstName?: string;

View File

@ -10,7 +10,7 @@ import Link from "next/link";
import { Button, Input, ErrorMessage } from "@/components/atoms"; import { Button, Input, ErrorMessage } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField"; import { FormField } from "@/components/molecules/FormField/FormField";
import { useLogin } from "../../hooks/use-auth"; 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 { useZodForm } from "@customer-portal/validation";
import { z } from "zod"; import { z } from "zod";

View File

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

View File

@ -11,7 +11,7 @@ import { Button, Input, ErrorMessage } from "@/components/atoms";
import { FormField } from "@/components/molecules/FormField/FormField"; import { FormField } from "@/components/molecules/FormField/FormField";
import { useWhmcsLink } from "../../hooks/use-auth"; import { useWhmcsLink } from "../../hooks/use-auth";
import { useZodForm } from "@customer-portal/validation"; 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"; import { z } from "zod";
interface SetPasswordFormProps { interface SetPasswordFormProps {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
"use client"; "use client";
import { formatCurrency } from "@customer-portal/domain"; import { formatCurrency } from "@customer-portal/domain/billing";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
export function InvoiceItemRow({ 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 { InvoiceTable } from "@/features/billing/components/InvoiceTable/InvoiceTable";
import { useInvoices } from "@/features/billing/hooks/useBilling"; import { useInvoices } from "@/features/billing/hooks/useBilling";
import { useSubscriptionInvoices } from "@/features/subscriptions/hooks/useSubscriptions"; 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"; import { cn } from "@/lib/utils";
interface InvoicesListProps { 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 { DataTable } from "@/components/molecules/DataTable/DataTable";
import { Button } from "@/components/atoms/button"; import { Button } from "@/components/atoms/button";
import { BillingStatusBadge } from "../BillingStatusBadge"; import { BillingStatusBadge } from "../BillingStatusBadge";
import type { Invoice } from "@customer-portal/domain"; import type { Invoice } from "@customer-portal/domain/billing";
import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain"; import { formatCurrency, getCurrencyLocale } from "@customer-portal/domain/billing";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { useCreateInvoiceSsoLink } from "@/features/billing/hooks/useBilling"; import { useCreateInvoiceSsoLink } from "@/features/billing/hooks/useBilling";
import { openSsoLink } from "@/features/billing/utils/sso"; import { openSsoLink } from "@/features/billing/utils/sso";

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { CreditCardIcon, BanknotesIcon, DevicePhoneMobileIcon, CheckCircleIcon } from "@heroicons/react/24/outline"; 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 { cn } from "@/lib/utils";
import type { ReactNode } from "react"; import type { ReactNode } from "react";

View File

@ -9,7 +9,7 @@ import {
ArrowPathIcon, ArrowPathIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { Badge } from "@/components/atoms/badge"; 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"; import { cn } from "@/lib/utils";
interface PaymentMethodCardProps extends React.HTMLAttributes<HTMLDivElement> { interface PaymentMethodCardProps extends React.HTMLAttributes<HTMLDivElement> {

View File

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

View File

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

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { CheckCircleIcon } from "@heroicons/react/24/solid"; 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"; import { getMonthlyPrice, getOneTimePrice } from "../../utils/pricing";
interface AddonGroupProps { interface AddonGroupProps {

View File

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

View File

@ -3,7 +3,7 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { MapPinIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline"; import { MapPinIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { useZodForm } from "@customer-portal/validation"; 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 { export interface AddressFormProps {
// Initial values // Initial values

View File

@ -12,7 +12,7 @@ import { Button } from "@/components/atoms/button";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
// Align with shared catalog contracts // 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 // Enhanced order item representation for UI summary
export type OrderItem = CatalogProductBase & { export type OrderItem = CatalogProductBase & {

View File

@ -1,5 +1,5 @@
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline"; 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 { useRouter } from "next/navigation";
import { Button } from "@/components/atoms/button"; import { Button } from "@/components/atoms/button";
import { getMonthlyPrice, getOneTimePrice } from "../../utils/pricing"; 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 { Button } from "@/components/atoms/button";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner"; import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { CreditCardIcon, CheckCircleIcon } from "@heroicons/react/24/outline"; 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 { export interface PaymentFormProps {
existingMethods?: PaymentMethod[]; existingMethods?: PaymentMethod[];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms"; import { StepHeader } from "@/components/atoms";
import { AddonGroup } from "@/features/catalog/components/base/AddonGroup"; import { AddonGroup } from "@/features/catalog/components/base/AddonGroup";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline"; 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 { interface Props {
addons: InternetAddonCatalogItem[]; addons: InternetAddonCatalogItem[];

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms"; import { StepHeader } from "@/components/atoms";
import { InstallationOptions } from "../../InstallationOptions"; import { InstallationOptions } from "../../InstallationOptions";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline"; 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 { interface Props {
installations: InternetInstallationCatalogItem[]; installations: InternetInstallationCatalogItem[];

View File

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

View File

@ -5,7 +5,7 @@ import { Button } from "@/components/atoms/button";
import { StepHeader } from "@/components/atoms"; import { StepHeader } from "@/components/atoms";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner"; import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { ArrowRightIcon } from "@heroicons/react/24/outline"; 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"; import type { AccessMode } from "../../../../hooks/useConfigureParams";
interface Props { interface Props {

View File

@ -3,7 +3,7 @@
import { DevicePhoneMobileIcon, UsersIcon, CurrencyYenIcon } from "@heroicons/react/24/outline"; import { DevicePhoneMobileIcon, UsersIcon, CurrencyYenIcon } from "@heroicons/react/24/outline";
import { AnimatedCard } from "@/components/molecules/AnimatedCard/AnimatedCard"; import { AnimatedCard } from "@/components/molecules/AnimatedCard/AnimatedCard";
import { Button } from "@/components/atoms/button"; 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"; import { getMonthlyPrice } from "../../utils/pricing";
interface SimPlanCardProps { interface SimPlanCardProps {

View File

@ -2,7 +2,7 @@
import React from "react"; import React from "react";
import { UsersIcon } from "@heroicons/react/24/outline"; 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"; import { SimPlanCard } from "./SimPlanCard";
interface SimPlanTypeSectionProps { interface SimPlanTypeSectionProps {

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ export * from "./hooks";
// Services // Services
export * from "./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 // Utilities
export * from "./utils"; export * from "./utils";

View File

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

View File

@ -3,7 +3,7 @@
* Helper functions for catalog operations * Helper functions for catalog operations
*/ */
import { formatCurrency } from "@customer-portal/domain"; import { formatCurrency } from "@customer-portal/domain/billing";
import type { import type {
CatalogFilter, CatalogFilter,
InternetPlanCatalogItem, InternetPlanCatalogItem,
@ -11,7 +11,7 @@ import type {
InternetInstallationCatalogItem, InternetInstallationCatalogItem,
SimCatalogProduct, SimCatalogProduct,
VpnCatalogProduct, VpnCatalogProduct,
} from "@customer-portal/domain"; } from "@customer-portal/domain/billing";
type CatalogProduct = type CatalogProduct =
| InternetPlanCatalogItem | 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 { export function getMonthlyPrice(product?: CatalogProductBase | null): number {
if (!product) return 0; if (!product) return 0;

View File

@ -17,7 +17,7 @@ import type {
InternetPlanCatalogItem, InternetPlanCatalogItem,
InternetInstallationCatalogItem, InternetInstallationCatalogItem,
InternetAddonCatalogItem, InternetAddonCatalogItem,
} from "@customer-portal/domain"; } from "@customer-portal/domain/billing";
import { getMonthlyPrice } from "../utils/pricing"; import { getMonthlyPrice } from "../utils/pricing";
import { LoadingCard, Skeleton, LoadingTable } from "@/components/atoms/loading-skeleton"; import { LoadingCard, Skeleton, LoadingTable } from "@/components/atoms/loading-skeleton";
import { AnimatedCard } from "@/components/molecules"; 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 { Button } from "@/components/atoms/button";
import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner"; import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner";
import { useSimCatalog } from "@/features/catalog/hooks"; 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"; import { SimPlanTypeSection } from "@/features/catalog/components/sim/SimPlanTypeSection";
interface PlansByType { interface PlansByType {

View File

@ -7,13 +7,13 @@ import { ordersService } from "@/features/orders/services/orders.service";
import { usePaymentMethods } from "@/features/billing/hooks/useBilling"; import { usePaymentMethods } from "@/features/billing/hooks/useBilling";
import { usePaymentRefresh } from "@/features/billing/hooks/usePaymentRefresh"; import { usePaymentRefresh } from "@/features/billing/hooks/usePaymentRefresh";
import { getMonthlyPrice, getOneTimePrice } from "@/features/catalog/utils/pricing"; import { getMonthlyPrice, getOneTimePrice } from "@/features/catalog/utils/pricing";
import type { CatalogProductBase } from "@customer-portal/domain"; import type { CatalogProductBase } from "@customer-portal/domain/billing";
import { createLoadingState, createSuccessState, createErrorState } from "@customer-portal/domain"; import { createLoadingState, createSuccessState, createErrorState } from "@customer-portal/domain/billing";
import type { AsyncState } from "@customer-portal/domain"; import type { AsyncState } from "@customer-portal/domain/billing";
import { useActiveSubscriptions } from "@/features/subscriptions/hooks/useSubscriptions"; import { useActiveSubscriptions } from "@/features/subscriptions/hooks/useSubscriptions";
// Use domain Address type // 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"; 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 { InlineToast } from "@/components/atoms/inline-toast";
import { StatusPill } from "@/components/atoms/status-pill"; import { StatusPill } from "@/components/atoms/status-pill";
import { AddressConfirmation } from "@/features/catalog/components/base/AddressConfirmation"; 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 { import {
ExclamationTriangleIcon, ExclamationTriangleIcon,
ShieldCheckIcon, ShieldCheckIcon,

View File

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

View File

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

View File

@ -6,7 +6,7 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useAuthSession } from "@/features/auth/services/auth.store"; import { useAuthSession } from "@/features/auth/services/auth.store";
import { apiClient, queryKeys, getDataOrThrow } from "@/lib/api"; 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 { class DashboardDataError extends Error {
constructor( constructor(

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { apiClient } from "@/lib/api"; 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> { async function createOrder<T = { sfOrderId: string }>(payload: CreateOrderRequest): Promise<T> {
const response = await apiClient.POST("/api/orders", { body: payload }); const response = await apiClient.POST("/api/orders", { body: payload });

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { apiClient, queryKeys, getDataOrDefault, getDataOrThrow, getNullableData } from "@/lib/api"; import { apiClient, queryKeys, getDataOrDefault, getDataOrThrow, getNullableData } from "@/lib/api";
import { useAuthSession } from "@/features/auth/services"; 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 { interface UseSubscriptionsOptions {
status?: string; status?: string;

View File

@ -20,7 +20,7 @@ import {
import { format } from "date-fns"; import { format } from "date-fns";
import { useSubscription } from "@/features/subscriptions/hooks"; import { useSubscription } from "@/features/subscriptions/hooks";
import { InvoicesList } from "@/features/billing/components/InvoiceList/InvoiceList"; 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"; import { SimManagementSection } from "@/features/sim-management";
export function SubscriptionDetailContainer() { export function SubscriptionDetailContainer() {

View File

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

View File

@ -91,12 +91,4 @@ export const authResponseSchema = z.object({
tokens: authTokensSchema, 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 CancelPlanApiRequest = Requests.FreebitCancelPlanApiRequest;
export type CancelAccountRequest = Requests.FreebitCancelAccountRequest; export type CancelAccountRequest = Requests.FreebitCancelAccountRequest;
export type AuthRequest = Requests.FreebitAuthRequest; 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 "./mapper";
export * from "./raw.types"; export * from "./raw.types";

View File

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

View File

@ -90,6 +90,37 @@ export const freebitCancelPlanApiRequestSchema = z.object({
runTime: z.string().optional(), 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({ export const freebitEsimReissueRequestSchema = z.object({
account: z.string().min(1, "Account is required"), account: z.string().min(1, "Account is required"),
newEid: z.string().min(1, "New EID is required"), newEid: z.string().min(1, "New EID is required"),
@ -98,9 +129,21 @@ export const freebitEsimReissueRequestSchema = z.object({
oldProductNumber: z.string().optional(), 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 // SIM Features
// ============================================================================ // =========================================================================
export const freebitSimFeaturesRequestSchema = z.object({ export const freebitSimFeaturesRequestSchema = z.object({
account: z.string().min(1, "Account is required"), 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 assign: z.boolean(), // true to assign, false to remove
}); });
// ============================================================================ // =========================================================================
// eSIM Activation // 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({ export const freebitAuthRequestSchema = z.object({
oemId: z.string().min(1), 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