/** * Communication Credits Hooks * For managing business SMS/calling credits and auto-reload settings */ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import apiClient from '../api/client'; export interface CommunicationCredits { id: number; balance_cents: number; auto_reload_enabled: boolean; auto_reload_threshold_cents: number; auto_reload_amount_cents: number; low_balance_warning_cents: number; low_balance_warning_sent: boolean; stripe_payment_method_id: string; last_twilio_sync_at: string | null; total_loaded_cents: number; total_spent_cents: number; created_at: string; updated_at: string; } export interface CreditTransaction { id: number; amount_cents: number; balance_after_cents: number; transaction_type: 'manual' | 'auto_reload' | 'usage' | 'refund' | 'adjustment' | 'promo'; description: string; reference_type: string; reference_id: string; stripe_charge_id: string; created_at: string; } export interface UpdateCreditsSettings { auto_reload_enabled?: boolean; auto_reload_threshold_cents?: number; auto_reload_amount_cents?: number; low_balance_warning_cents?: number; stripe_payment_method_id?: string; } export interface AddCreditsRequest { amount_cents: number; payment_method_id?: string; } /** * Hook to get communication credits for current business */ export const useCommunicationCredits = () => { return useQuery({ queryKey: ['communicationCredits'], queryFn: async () => { const { data } = await apiClient.get('/communication-credits/'); return data; }, staleTime: 30 * 1000, // 30 seconds }); }; /** * Hook to get credit transaction history */ export const useCreditTransactions = (page = 1, limit = 20) => { return useQuery<{ results: CreditTransaction[]; count: number; next: string | null; previous: string | null }>({ queryKey: ['creditTransactions', page, limit], queryFn: async () => { const { data } = await apiClient.get('/communication-credits/transactions/', { params: { page, limit }, }); return data; }, staleTime: 30 * 1000, }); }; /** * Hook to update credit settings (auto-reload, thresholds, etc.) */ export const useUpdateCreditsSettings = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (settings: UpdateCreditsSettings) => { const { data } = await apiClient.patch('/communication-credits/settings/', settings); return data; }, onSuccess: (data) => { queryClient.setQueryData(['communicationCredits'], data); }, }); }; /** * Hook to add credits (manual top-up) */ export const useAddCredits = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (request: AddCreditsRequest) => { const { data } = await apiClient.post('/communication-credits/add/', request); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['communicationCredits'] }); queryClient.invalidateQueries({ queryKey: ['creditTransactions'] }); }, }); }; /** * Hook to create a payment intent for credit purchase */ export const useCreatePaymentIntent = () => { return useMutation({ mutationFn: async (amount_cents: number) => { const { data } = await apiClient.post('/communication-credits/create-payment-intent/', { amount_cents, }); return data as { client_secret: string; payment_intent_id: string }; }, }); }; /** * Hook to confirm a payment after client-side processing */ export const useConfirmPayment = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (params: { payment_intent_id: string; save_payment_method?: boolean }) => { const { data } = await apiClient.post('/communication-credits/confirm-payment/', params); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['communicationCredits'] }); queryClient.invalidateQueries({ queryKey: ['creditTransactions'] }); }, }); }; /** * Hook to set up Stripe payment method for auto-reload */ export const useSetupPaymentMethod = () => { return useMutation({ mutationFn: async () => { const { data } = await apiClient.post('/communication-credits/setup-payment-method/'); return data as { client_secret: string }; }, }); }; /** * Hook to save a payment method after setup */ export const useSavePaymentMethod = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (payment_method_id: string) => { const { data } = await apiClient.post('/communication-credits/save-payment-method/', { payment_method_id, }); return data; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['communicationCredits'] }); }, }); }; /** * Hook to get communication usage stats */ export const useCommunicationUsageStats = () => { return useQuery<{ sms_sent_this_month: number; voice_minutes_this_month: number; proxy_numbers_active: number; estimated_cost_cents: number; }>({ queryKey: ['communicationUsageStats'], queryFn: async () => { const { data } = await apiClient.get('/communication-credits/usage-stats/'); return data; }, staleTime: 60 * 1000, // 1 minute }); }; // ============================================================================= // Phone Number Management Hooks // ============================================================================= export interface ProxyPhoneNumber { id: number; phone_number: string; friendly_name: string; status: 'available' | 'assigned' | 'reserved' | 'inactive'; monthly_fee_cents: number; capabilities: { voice: boolean; sms: boolean; mms: boolean; }; assigned_at: string | null; last_billed_at: string | null; } export interface AvailablePhoneNumber { phone_number: string; friendly_name: string; locality: string; region: string; postal_code: string; capabilities: { voice: boolean; sms: boolean; mms: boolean; }; monthly_cost_cents: number; } /** * Hook to list phone numbers assigned to the tenant */ export const usePhoneNumbers = () => { return useQuery<{ numbers: ProxyPhoneNumber[]; count: number }>({ queryKey: ['phoneNumbers'], queryFn: async () => { const { data } = await apiClient.get('/communication-credits/phone-numbers/'); return data; }, staleTime: 30 * 1000, }); }; /** * Hook to search for available phone numbers from Twilio */ export const useSearchPhoneNumbers = () => { return useMutation({ mutationFn: async (params: { area_code?: string; contains?: string; country?: string; limit?: number; }) => { const { data } = await apiClient.get('/communication-credits/phone-numbers/search/', { params, }); return data as { numbers: AvailablePhoneNumber[]; count: number }; }, }); }; /** * Hook to purchase a phone number */ export const usePurchasePhoneNumber = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (params: { phone_number: string; friendly_name?: string }) => { const { data } = await apiClient.post('/communication-credits/phone-numbers/purchase/', params); return data as { success: boolean; phone_number: ProxyPhoneNumber; balance_cents: number; }; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['phoneNumbers'] }); queryClient.invalidateQueries({ queryKey: ['communicationCredits'] }); queryClient.invalidateQueries({ queryKey: ['creditTransactions'] }); }, }); }; /** * Hook to release (delete) a phone number */ export const useReleasePhoneNumber = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (numberId: number) => { const { data } = await apiClient.delete(`/communication-credits/phone-numbers/${numberId}/`); return data as { success: boolean; message: string }; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['phoneNumbers'] }); queryClient.invalidateQueries({ queryKey: ['communicationUsageStats'] }); }, }); }; /** * Hook to change a phone number to a different one */ export const useChangePhoneNumber = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (params: { numberId: number; new_phone_number: string; friendly_name?: string }) => { const { numberId, ...body } = params; const { data } = await apiClient.post(`/communication-credits/phone-numbers/${numberId}/change/`, body); return data as { success: boolean; phone_number: ProxyPhoneNumber; balance_cents: number; }; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['phoneNumbers'] }); queryClient.invalidateQueries({ queryKey: ['communicationCredits'] }); queryClient.invalidateQueries({ queryKey: ['creditTransactions'] }); }, }); };