178 lines
5.1 KiB
TypeScript
178 lines
5.1 KiB
TypeScript
|
|
/**
|
||
|
|
* Subscriptions Hooks
|
||
|
|
* React hooks for subscription functionality using shared types
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||
|
|
import { useAuthStore } from "@/features/auth/services/auth.store";
|
||
|
|
import { openApiClient as apiClient } from "@/lib/api/openapi-client";
|
||
|
|
import { unwrap } from "@/lib/api/unwrap";
|
||
|
|
import type { Subscription, SubscriptionList, InvoiceList } from "@customer-portal/domain";
|
||
|
|
|
||
|
|
interface UseSubscriptionsOptions {
|
||
|
|
status?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook to fetch all subscriptions
|
||
|
|
*/
|
||
|
|
export function useSubscriptions(options: UseSubscriptionsOptions = {}) {
|
||
|
|
const { status } = options;
|
||
|
|
const { tokens, isAuthenticated } = useAuthStore();
|
||
|
|
|
||
|
|
return useQuery<SubscriptionList | Subscription[]>({
|
||
|
|
queryKey: ["subscriptions", status],
|
||
|
|
queryFn: async () => {
|
||
|
|
if (!tokens?.accessToken) {
|
||
|
|
throw new Error("Authentication required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const params = new URLSearchParams({
|
||
|
|
...(status && { status }),
|
||
|
|
});
|
||
|
|
const res = await apiClient.get<SubscriptionList | Subscription[]>(
|
||
|
|
`/subscriptions?${params}`
|
||
|
|
);
|
||
|
|
return unwrap(res) as SubscriptionList | Subscription[];
|
||
|
|
},
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||
|
|
enabled: isAuthenticated && !!tokens?.accessToken,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook to fetch active subscriptions only
|
||
|
|
*/
|
||
|
|
export function useActiveSubscriptions() {
|
||
|
|
const { tokens, isAuthenticated } = useAuthStore();
|
||
|
|
|
||
|
|
return useQuery<Subscription[]>({
|
||
|
|
queryKey: ["subscriptions", "active"],
|
||
|
|
queryFn: async () => {
|
||
|
|
if (!tokens?.accessToken) {
|
||
|
|
throw new Error("Authentication required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const res = await apiClient.get<Subscription[]>(`/subscriptions/active`);
|
||
|
|
return unwrap(res) as Subscription[];
|
||
|
|
},
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||
|
|
enabled: isAuthenticated && !!tokens?.accessToken,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook to fetch subscription statistics
|
||
|
|
*/
|
||
|
|
export function useSubscriptionStats() {
|
||
|
|
const { tokens, isAuthenticated } = useAuthStore();
|
||
|
|
|
||
|
|
return useQuery<{
|
||
|
|
total: number;
|
||
|
|
active: number;
|
||
|
|
suspended: number;
|
||
|
|
cancelled: number;
|
||
|
|
pending: number;
|
||
|
|
}>({
|
||
|
|
queryKey: ["subscriptions", "stats"],
|
||
|
|
queryFn: async () => {
|
||
|
|
if (!tokens?.accessToken) {
|
||
|
|
throw new Error("Authentication required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const res = await apiClient.get<{
|
||
|
|
total: number;
|
||
|
|
active: number;
|
||
|
|
suspended: number;
|
||
|
|
cancelled: number;
|
||
|
|
pending: number;
|
||
|
|
}>(`/subscriptions/stats`);
|
||
|
|
return unwrap(res) as {
|
||
|
|
total: number;
|
||
|
|
active: number;
|
||
|
|
suspended: number;
|
||
|
|
cancelled: number;
|
||
|
|
pending: number;
|
||
|
|
};
|
||
|
|
},
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||
|
|
enabled: isAuthenticated && !!tokens?.accessToken,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook to fetch a specific subscription
|
||
|
|
*/
|
||
|
|
export function useSubscription(subscriptionId: number) {
|
||
|
|
const { tokens, isAuthenticated } = useAuthStore();
|
||
|
|
|
||
|
|
return useQuery<Subscription>({
|
||
|
|
queryKey: ["subscription", subscriptionId],
|
||
|
|
queryFn: async () => {
|
||
|
|
if (!tokens?.accessToken) {
|
||
|
|
throw new Error("Authentication required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const res = await apiClient.get<Subscription>(`/subscriptions/${subscriptionId}`);
|
||
|
|
return unwrap(res) as Subscription;
|
||
|
|
},
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
gcTime: 10 * 60 * 1000, // 10 minutes
|
||
|
|
enabled: isAuthenticated && !!tokens?.accessToken,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook to fetch subscription invoices
|
||
|
|
*/
|
||
|
|
export function useSubscriptionInvoices(
|
||
|
|
subscriptionId: number,
|
||
|
|
options: { page?: number; limit?: number } = {}
|
||
|
|
) {
|
||
|
|
const { page = 1, limit = 10 } = options;
|
||
|
|
const { tokens, isAuthenticated } = useAuthStore();
|
||
|
|
|
||
|
|
return useQuery<InvoiceList>({
|
||
|
|
queryKey: ["subscription-invoices", subscriptionId, page, limit],
|
||
|
|
queryFn: async () => {
|
||
|
|
if (!tokens?.accessToken) {
|
||
|
|
throw new Error("Authentication required");
|
||
|
|
}
|
||
|
|
|
||
|
|
const params = new URLSearchParams({
|
||
|
|
page: page.toString(),
|
||
|
|
limit: limit.toString(),
|
||
|
|
});
|
||
|
|
const res = await apiClient.get<InvoiceList>(
|
||
|
|
`/subscriptions/${subscriptionId}/invoices?${params}`
|
||
|
|
);
|
||
|
|
return unwrap(res) as InvoiceList;
|
||
|
|
},
|
||
|
|
staleTime: 60 * 1000, // 1 minute
|
||
|
|
gcTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
enabled: isAuthenticated && !!tokens?.accessToken && !!subscriptionId,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 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 }) => {
|
||
|
|
const res = await apiClient.post(`/subscriptions/${id}/actions`, { action });
|
||
|
|
return unwrap(res);
|
||
|
|
},
|
||
|
|
onSuccess: (_, { id }) => {
|
||
|
|
// Invalidate relevant queries after successful action
|
||
|
|
void queryClient.invalidateQueries({ queryKey: ["subscriptions"] });
|
||
|
|
void queryClient.invalidateQueries({ queryKey: ["subscription", id] });
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|