This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
198 lines
5.4 KiB
TypeScript
198 lines
5.4 KiB
TypeScript
/**
|
|
* Transaction Analytics Hooks
|
|
*
|
|
* React Query hooks for fetching and managing transaction analytics data.
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import {
|
|
getTransactions,
|
|
getTransaction,
|
|
getTransactionSummary,
|
|
getStripeCharges,
|
|
getStripePayouts,
|
|
getStripeBalance,
|
|
exportTransactions,
|
|
getTransactionDetail,
|
|
refundTransaction,
|
|
TransactionFilters,
|
|
ExportRequest,
|
|
RefundRequest,
|
|
} from '../api/payments';
|
|
|
|
/**
|
|
* Hook to fetch paginated transaction list with optional filters.
|
|
*/
|
|
export const useTransactions = (filters?: TransactionFilters) => {
|
|
return useQuery({
|
|
queryKey: ['transactions', filters],
|
|
queryFn: async () => {
|
|
const { data } = await getTransactions(filters);
|
|
return data;
|
|
},
|
|
staleTime: 30 * 1000, // 30 seconds
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch a single transaction by ID.
|
|
*/
|
|
export const useTransaction = (id: number) => {
|
|
return useQuery({
|
|
queryKey: ['transaction', id],
|
|
queryFn: async () => {
|
|
const { data } = await getTransaction(id);
|
|
return data;
|
|
},
|
|
enabled: !!id,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch transaction summary/analytics.
|
|
*/
|
|
export const useTransactionSummary = (filters?: Pick<TransactionFilters, 'start_date' | 'end_date'>) => {
|
|
return useQuery({
|
|
queryKey: ['transactionSummary', filters],
|
|
queryFn: async () => {
|
|
const { data } = await getTransactionSummary(filters);
|
|
return data;
|
|
},
|
|
staleTime: 60 * 1000, // 1 minute
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch Stripe charges directly from Stripe API.
|
|
*/
|
|
export const useStripeCharges = (limit: number = 20) => {
|
|
return useQuery({
|
|
queryKey: ['stripeCharges', limit],
|
|
queryFn: async () => {
|
|
const { data } = await getStripeCharges(limit);
|
|
return data;
|
|
},
|
|
staleTime: 30 * 1000,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch Stripe payouts.
|
|
*/
|
|
export const useStripePayouts = (limit: number = 20) => {
|
|
return useQuery({
|
|
queryKey: ['stripePayouts', limit],
|
|
queryFn: async () => {
|
|
const { data } = await getStripePayouts(limit);
|
|
return data;
|
|
},
|
|
staleTime: 30 * 1000,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch current Stripe balance.
|
|
*/
|
|
export const useStripeBalance = () => {
|
|
return useQuery({
|
|
queryKey: ['stripeBalance'],
|
|
queryFn: async () => {
|
|
const { data } = await getStripeBalance();
|
|
return data;
|
|
},
|
|
staleTime: 60 * 1000, // 1 minute
|
|
refetchInterval: 5 * 60 * 1000, // Refresh every 5 minutes
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to export transaction data.
|
|
* Returns a mutation that triggers file download.
|
|
*/
|
|
export const useExportTransactions = () => {
|
|
return useMutation({
|
|
mutationFn: async (request: ExportRequest) => {
|
|
const response = await exportTransactions(request);
|
|
return response;
|
|
},
|
|
onSuccess: (response, request) => {
|
|
// Create blob URL and trigger download
|
|
const blob = new Blob([response.data], { type: response.headers['content-type'] });
|
|
const url = window.URL.createObjectURL(blob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
|
|
// Determine file extension based on format
|
|
const extensions: Record<string, string> = {
|
|
csv: 'csv',
|
|
xlsx: 'xlsx',
|
|
pdf: 'pdf',
|
|
quickbooks: 'iif',
|
|
};
|
|
const ext = extensions[request.format] || 'txt';
|
|
link.download = `transactions.${ext}`;
|
|
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
window.URL.revokeObjectURL(url);
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to invalidate all transaction-related queries.
|
|
* Useful after actions that modify transaction data.
|
|
*/
|
|
export const useInvalidateTransactions = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return () => {
|
|
queryClient.invalidateQueries({ queryKey: ['transactions'] });
|
|
queryClient.invalidateQueries({ queryKey: ['transactionSummary'] });
|
|
queryClient.invalidateQueries({ queryKey: ['stripeCharges'] });
|
|
queryClient.invalidateQueries({ queryKey: ['stripePayouts'] });
|
|
queryClient.invalidateQueries({ queryKey: ['stripeBalance'] });
|
|
queryClient.invalidateQueries({ queryKey: ['transactionDetail'] });
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Hook to fetch detailed transaction information including refund data.
|
|
*/
|
|
export const useTransactionDetail = (id: number | null) => {
|
|
return useQuery({
|
|
queryKey: ['transactionDetail', id],
|
|
queryFn: async () => {
|
|
if (!id) return null;
|
|
const { data } = await getTransactionDetail(id);
|
|
return data;
|
|
},
|
|
enabled: !!id,
|
|
staleTime: 10 * 1000, // 10 seconds (refresh often for live data)
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Hook to issue a refund for a transaction.
|
|
* Automatically invalidates transaction queries on success.
|
|
*/
|
|
export const useRefundTransaction = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ transactionId, request }: { transactionId: number; request?: RefundRequest }) => {
|
|
const { data } = await refundTransaction(transactionId, request);
|
|
return data;
|
|
},
|
|
onSuccess: (data, variables) => {
|
|
// Invalidate all relevant queries
|
|
queryClient.invalidateQueries({ queryKey: ['transactions'] });
|
|
queryClient.invalidateQueries({ queryKey: ['transactionSummary'] });
|
|
queryClient.invalidateQueries({ queryKey: ['transactionDetail', variables.transactionId] });
|
|
queryClient.invalidateQueries({ queryKey: ['stripeCharges'] });
|
|
queryClient.invalidateQueries({ queryKey: ['stripeBalance'] });
|
|
},
|
|
});
|
|
};
|