barsa aaabb795c1 Integrate authentication checks in billing hooks and enhance UI components
- Added authentication checks in useInvoices, useInvoice, and usePaymentMethods hooks to ensure data fetching only occurs for authenticated users.
- Updated usePaymentRefresh to prevent refresh actions when the user is not authenticated.
- Refactored AddressConfirmation component to improve button layout and accessibility.
- Enhanced InternetPlanCard to format plan names for clearer presentation.
- Streamlined InternetConfigureContainer and related components to utilize Zustand for state management, improving code clarity and maintainability.
- Updated SimConfigureView to simplify step transitions and improve user experience.
2025-10-22 16:52:07 +09:00

216 lines
5.8 KiB
TypeScript

"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<typeof queryKeys.billing.invoices>;
type InvoiceQueryKey = ReturnType<typeof queryKeys.billing.invoice>;
type PaymentMethodsQueryKey = ReturnType<typeof queryKeys.billing.paymentMethods>;
type InvoicesQueryOptions = Omit<
UseQueryOptions<InvoiceList, Error, InvoiceList, InvoicesQueryKey>,
"queryKey" | "queryFn"
>;
type InvoiceQueryOptions = Omit<
UseQueryOptions<Invoice, Error, Invoice, InvoiceQueryKey>,
"queryKey" | "queryFn"
>;
type PaymentMethodsQueryOptions = Omit<
UseQueryOptions<PaymentMethodList, Error, PaymentMethodList, PaymentMethodsQueryKey>,
"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<InvoiceList> {
const query = params ? toQueryParams(params) : undefined;
const response = await apiClient.GET<InvoiceList>(
"/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<Invoice> {
const response = await apiClient.GET<Invoice>("/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<PaymentMethodList> {
const response = await apiClient.GET<PaymentMethodList>("/api/invoices/payment-methods");
return getDataOrDefault(response, EMPTY_PAYMENT_METHODS);
}
// Exported hooks
export function useInvoices(
params?: InvoiceQueryParams,
options?: InvoicesQueryOptions
): UseQueryResult<InvoiceList, Error> {
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<Invoice, Error> {
const { isAuthenticated } = useAuthSession();
return useQuery({
queryKey: queryKeys.billing.invoice(id),
queryFn: () => fetchInvoice(id),
enabled: isAuthenticated && Boolean(id),
...options,
});
}
export function usePaymentMethods(
options?: PaymentMethodsQueryOptions
): UseQueryResult<PaymentMethodList, Error> {
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<InvoiceSsoLink>("/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<InvoiceSsoLink, Error, void>
): UseMutationResult<InvoiceSsoLink, Error, void> {
return useMutation({
mutationFn: async () => {
const response = await apiClient.POST<InvoiceSsoLink>("/auth/sso-link", {
body: { destination: "index.php?rp=/account/paymentmethods" },
});
return getDataOrThrow(response, "Failed to create payment methods SSO link");
},
...options,
});
}