"use client"; import { useMutation, useQuery, type UseMutationOptions, type UseMutationResult, type UseQueryOptions, type UseQueryResult, } from "@tanstack/react-query"; // ✅ Generic utilities from lib import { apiClient, queryKeys, getDataOrDefault, getDataOrThrow, type QueryParams, } from "@/lib/api"; // ✅ Single consolidated import from domain import { // Types type Invoice, type InvoiceList, type InvoiceSsoLink, type InvoiceQueryParams, type InvoiceStatus, // Schemas invoiceSchema, invoiceListSchema, // Constants INVOICE_STATUS, } from "@customer-portal/domain/billing"; import { type PaymentMethodList } from "@customer-portal/domain/payments"; import { useAuthSession } from "@/features/auth/services/auth.store"; // Constants const EMPTY_INVOICE_LIST: InvoiceList = { invoices: [], pagination: { page: 1, totalItems: 0, totalPages: 0, }, }; const EMPTY_PAYMENT_METHODS: PaymentMethodList = { paymentMethods: [], totalCount: 0, }; const FALLBACK_STATUS: InvoiceStatus = INVOICE_STATUS.DRAFT; function ensureInvoiceStatus(invoice: Invoice): Invoice { return { ...invoice, status: (invoice.status as InvoiceStatus | undefined) ?? FALLBACK_STATUS, }; } function normalizeInvoiceList(list: InvoiceList): InvoiceList { return { ...list, invoices: list.invoices.map(ensureInvoiceStatus), pagination: { page: list.pagination?.page ?? 1, totalItems: list.pagination?.totalItems ?? 0, totalPages: list.pagination?.totalPages ?? 0, nextCursor: list.pagination?.nextCursor, }, }; } // Type helpers for React Query type InvoicesQueryKey = ReturnType; type InvoiceQueryKey = ReturnType; type PaymentMethodsQueryKey = ReturnType; type InvoicesQueryOptions = Omit< UseQueryOptions, "queryKey" | "queryFn" >; type InvoiceQueryOptions = Omit< UseQueryOptions, "queryKey" | "queryFn" >; type PaymentMethodsQueryOptions = Omit< UseQueryOptions, "queryKey" | "queryFn" >; type SsoLinkMutationOptions = UseMutationOptions< InvoiceSsoLink, Error, { invoiceId: number; target?: "view" | "download" | "pay" } >; // Helper functions function toQueryParams(params: InvoiceQueryParams): QueryParams { const query: QueryParams = {}; for (const [key, value] of Object.entries(params)) { if (value === undefined) { continue; } if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { query[key] = value; } } return query; } // API functions async function fetchInvoices(params?: InvoiceQueryParams): Promise { const query = params ? toQueryParams(params) : undefined; const response = await apiClient.GET( "/api/invoices", query ? { params: { query } } : undefined ); const data = getDataOrDefault(response, EMPTY_INVOICE_LIST); const parsed = invoiceListSchema.parse(data); return normalizeInvoiceList(parsed); } async function fetchInvoice(id: string): Promise { const response = await apiClient.GET("/api/invoices/{id}", { params: { path: { id } }, }); const invoice = getDataOrThrow(response, "Invoice not found"); const parsed = invoiceSchema.parse(invoice); return ensureInvoiceStatus(parsed); } async function fetchPaymentMethods(): Promise { const response = await apiClient.GET("/api/invoices/payment-methods"); return getDataOrDefault(response, EMPTY_PAYMENT_METHODS); } // Exported hooks export function useInvoices( params?: InvoiceQueryParams, options?: InvoicesQueryOptions ): UseQueryResult { const { isAuthenticated } = useAuthSession(); const queryKeyParams = params ? { ...params } : undefined; return useQuery({ queryKey: queryKeys.billing.invoices(queryKeyParams), queryFn: () => fetchInvoices(params), enabled: isAuthenticated, ...options, }); } export function useInvoice( id: string, options?: InvoiceQueryOptions ): UseQueryResult { const { isAuthenticated } = useAuthSession(); return useQuery({ queryKey: queryKeys.billing.invoice(id), queryFn: () => fetchInvoice(id), enabled: isAuthenticated && Boolean(id), ...options, }); } export function usePaymentMethods( options?: PaymentMethodsQueryOptions ): UseQueryResult { const { isAuthenticated } = useAuthSession(); return useQuery({ queryKey: queryKeys.billing.paymentMethods(), queryFn: fetchPaymentMethods, enabled: isAuthenticated, ...options, }); } export function useCreateInvoiceSsoLink( options?: SsoLinkMutationOptions ): UseMutationResult< InvoiceSsoLink, Error, { invoiceId: number; target?: "view" | "download" | "pay" } > { return useMutation({ mutationFn: async ({ invoiceId, target }) => { const response = await apiClient.POST("/api/invoices/{id}/sso-link", { params: { path: { id: invoiceId }, query: target ? { target } : undefined, }, }); return getDataOrThrow(response, "Failed to create SSO link"); }, ...options, }); } export function useCreatePaymentMethodsSsoLink( options?: UseMutationOptions ): UseMutationResult { return useMutation({ mutationFn: async () => { const response = await apiClient.POST("/auth/sso-link", { body: { destination: "index.php?rp=/account/paymentmethods" }, }); return getDataOrThrow(response, "Failed to create payment methods SSO link"); }, ...options, }); }