2025-09-17 18:43:43 +09:00
|
|
|
/**
|
|
|
|
|
* Subscriptions Hooks
|
|
|
|
|
* React hooks for subscription functionality using shared types
|
|
|
|
|
*/
|
|
|
|
|
|
2025-09-18 17:49:43 +09:00
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
import { apiClient, queryKeys } from "@/core/api";
|
2025-09-18 16:23:56 +09:00
|
|
|
import { useAuthSession } from "@/features/auth/services/auth.store";
|
2025-09-18 17:49:43 +09:00
|
|
|
import type { InvoiceList, Subscription, SubscriptionList } from "@customer-portal/domain";
|
2025-09-17 18:43:43 +09:00
|
|
|
|
|
|
|
|
interface UseSubscriptionsOptions {
|
|
|
|
|
status?: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-18 17:49:43 +09:00
|
|
|
const emptySubscriptionList: SubscriptionList = {
|
|
|
|
|
subscriptions: [],
|
|
|
|
|
totalCount: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const emptyStats = {
|
|
|
|
|
total: 0,
|
|
|
|
|
active: 0,
|
|
|
|
|
suspended: 0,
|
|
|
|
|
cancelled: 0,
|
|
|
|
|
pending: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const emptyInvoiceList: InvoiceList = {
|
|
|
|
|
invoices: [],
|
|
|
|
|
pagination: {
|
|
|
|
|
page: 1,
|
|
|
|
|
totalItems: 0,
|
|
|
|
|
totalPages: 0,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function toSubscriptionList(
|
|
|
|
|
payload?: SubscriptionList | Subscription[] | null
|
|
|
|
|
): SubscriptionList {
|
|
|
|
|
if (!payload) {
|
|
|
|
|
return emptySubscriptionList;
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(payload)) {
|
|
|
|
|
return {
|
|
|
|
|
subscriptions: payload,
|
|
|
|
|
totalCount: payload.length,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return payload;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 18:43:43 +09:00
|
|
|
/**
|
|
|
|
|
* Hook to fetch all subscriptions
|
|
|
|
|
*/
|
|
|
|
|
export function useSubscriptions(options: UseSubscriptionsOptions = {}) {
|
|
|
|
|
const { status } = options;
|
2025-09-18 16:23:56 +09:00
|
|
|
const { isAuthenticated, hasValidToken } = useAuthSession();
|
2025-09-17 18:43:43 +09:00
|
|
|
|
2025-09-18 17:49:43 +09:00
|
|
|
return useQuery<SubscriptionList>({
|
|
|
|
|
queryKey: queryKeys.subscriptions.list(status ? { status } : {}),
|
2025-09-17 18:43:43 +09:00
|
|
|
queryFn: async () => {
|
2025-09-18 16:23:56 +09:00
|
|
|
const response = await apiClient.GET(
|
|
|
|
|
"/subscriptions",
|
|
|
|
|
status ? { params: { query: { status } } } : undefined
|
2025-09-17 18:43:43 +09:00
|
|
|
);
|
2025-09-18 17:49:43 +09:00
|
|
|
return toSubscriptionList(response.data);
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
|
|
gcTime: 10 * 60 * 1000,
|
2025-09-18 16:23:56 +09:00
|
|
|
enabled: isAuthenticated && hasValidToken,
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to fetch active subscriptions only
|
|
|
|
|
*/
|
|
|
|
|
export function useActiveSubscriptions() {
|
2025-09-18 16:23:56 +09:00
|
|
|
const { isAuthenticated, hasValidToken } = useAuthSession();
|
2025-09-17 18:43:43 +09:00
|
|
|
|
|
|
|
|
return useQuery<Subscription[]>({
|
2025-09-18 17:49:43 +09:00
|
|
|
queryKey: [...queryKeys.subscriptions.all, "active"] as const,
|
2025-09-17 18:43:43 +09:00
|
|
|
queryFn: async () => {
|
2025-09-18 16:23:56 +09:00
|
|
|
const response = await apiClient.GET("/subscriptions/active");
|
2025-09-18 17:49:43 +09:00
|
|
|
return response.data ?? [];
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
|
|
gcTime: 10 * 60 * 1000,
|
2025-09-18 16:23:56 +09:00
|
|
|
enabled: isAuthenticated && hasValidToken,
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to fetch subscription statistics
|
|
|
|
|
*/
|
|
|
|
|
export function useSubscriptionStats() {
|
2025-09-18 16:23:56 +09:00
|
|
|
const { isAuthenticated, hasValidToken } = useAuthSession();
|
2025-09-17 18:43:43 +09:00
|
|
|
|
2025-09-18 17:49:43 +09:00
|
|
|
return useQuery({
|
|
|
|
|
queryKey: queryKeys.subscriptions.stats(),
|
2025-09-17 18:43:43 +09:00
|
|
|
queryFn: async () => {
|
2025-09-18 16:23:56 +09:00
|
|
|
const response = await apiClient.GET("/subscriptions/stats");
|
2025-09-18 17:49:43 +09:00
|
|
|
return response.data ?? emptyStats;
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
|
|
gcTime: 10 * 60 * 1000,
|
2025-09-18 16:23:56 +09:00
|
|
|
enabled: isAuthenticated && hasValidToken,
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to fetch a specific subscription
|
|
|
|
|
*/
|
|
|
|
|
export function useSubscription(subscriptionId: number) {
|
2025-09-18 16:23:56 +09:00
|
|
|
const { isAuthenticated, hasValidToken } = useAuthSession();
|
2025-09-17 18:43:43 +09:00
|
|
|
|
|
|
|
|
return useQuery<Subscription>({
|
2025-09-18 17:49:43 +09:00
|
|
|
queryKey: queryKeys.subscriptions.detail(String(subscriptionId)),
|
2025-09-17 18:43:43 +09:00
|
|
|
queryFn: async () => {
|
2025-09-18 16:23:56 +09:00
|
|
|
const response = await apiClient.GET("/subscriptions/{id}", {
|
|
|
|
|
params: { path: { id: subscriptionId } },
|
|
|
|
|
});
|
|
|
|
|
if (!response.data) {
|
|
|
|
|
throw new Error("Subscription not found");
|
|
|
|
|
}
|
2025-09-18 17:49:43 +09:00
|
|
|
return response.data;
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
staleTime: 5 * 60 * 1000,
|
|
|
|
|
gcTime: 10 * 60 * 1000,
|
|
|
|
|
enabled: isAuthenticated && hasValidToken && subscriptionId > 0,
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to fetch subscription invoices
|
|
|
|
|
*/
|
|
|
|
|
export function useSubscriptionInvoices(
|
|
|
|
|
subscriptionId: number,
|
|
|
|
|
options: { page?: number; limit?: number } = {}
|
|
|
|
|
) {
|
|
|
|
|
const { page = 1, limit = 10 } = options;
|
2025-09-18 16:23:56 +09:00
|
|
|
const { isAuthenticated, hasValidToken } = useAuthSession();
|
2025-09-17 18:43:43 +09:00
|
|
|
|
|
|
|
|
return useQuery<InvoiceList>({
|
2025-09-18 17:49:43 +09:00
|
|
|
queryKey: queryKeys.subscriptions.invoices(subscriptionId, { page, limit }),
|
2025-09-17 18:43:43 +09:00
|
|
|
queryFn: async () => {
|
2025-09-18 16:23:56 +09:00
|
|
|
const response = await apiClient.GET("/subscriptions/{id}/invoices", {
|
|
|
|
|
params: {
|
|
|
|
|
path: { id: subscriptionId },
|
|
|
|
|
query: { page, limit },
|
|
|
|
|
},
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
2025-09-18 17:49:43 +09:00
|
|
|
if (!response.data) {
|
|
|
|
|
return {
|
|
|
|
|
...emptyInvoiceList,
|
2025-09-18 16:23:56 +09:00
|
|
|
pagination: {
|
2025-09-18 17:49:43 +09:00
|
|
|
...emptyInvoiceList.pagination,
|
2025-09-18 16:23:56 +09:00
|
|
|
page,
|
|
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
staleTime: 60 * 1000,
|
|
|
|
|
gcTime: 5 * 60 * 1000,
|
|
|
|
|
enabled: isAuthenticated && hasValidToken && subscriptionId > 0,
|
2025-09-17 18:43:43 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook to perform subscription actions (suspend, resume, cancel, etc.)
|
|
|
|
|
*/
|
|
|
|
|
export function useSubscriptionAction() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async ({ id, action }: { id: number; action: string }) => {
|
2025-09-18 16:23:56 +09:00
|
|
|
await apiClient.POST("/subscriptions/{id}/actions", {
|
|
|
|
|
params: { path: { id } },
|
|
|
|
|
body: { action },
|
|
|
|
|
});
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
2025-09-18 17:49:43 +09:00
|
|
|
onSuccess: async () => {
|
|
|
|
|
await queryClient.invalidateQueries({ queryKey: queryKeys.subscriptions.all });
|
2025-09-17 18:43:43 +09:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|