190 lines
6.6 KiB
TypeScript
190 lines
6.6 KiB
TypeScript
|
|
/**
|
||
|
|
* Sales React Query hooks
|
||
|
|
*/
|
||
|
|
import { useMutation, useQuery, useQueryClient, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query';
|
||
|
|
import { salesService } from '../services/sales';
|
||
|
|
import {
|
||
|
|
SalesDataCreate,
|
||
|
|
SalesDataUpdate,
|
||
|
|
SalesDataResponse,
|
||
|
|
SalesDataQuery,
|
||
|
|
SalesAnalytics,
|
||
|
|
} from '../types/sales';
|
||
|
|
import { ApiError } from '../client';
|
||
|
|
|
||
|
|
// Query Keys
|
||
|
|
export const salesKeys = {
|
||
|
|
all: ['sales'] as const,
|
||
|
|
lists: () => [...salesKeys.all, 'list'] as const,
|
||
|
|
list: (tenantId: string, filters?: SalesDataQuery) => [...salesKeys.lists(), tenantId, filters] as const,
|
||
|
|
details: () => [...salesKeys.all, 'detail'] as const,
|
||
|
|
detail: (tenantId: string, recordId: string) => [...salesKeys.details(), tenantId, recordId] as const,
|
||
|
|
analytics: (tenantId: string, startDate?: string, endDate?: string) =>
|
||
|
|
[...salesKeys.all, 'analytics', tenantId, { startDate, endDate }] as const,
|
||
|
|
productSales: (tenantId: string, productId: string, startDate?: string, endDate?: string) =>
|
||
|
|
[...salesKeys.all, 'product-sales', tenantId, productId, { startDate, endDate }] as const,
|
||
|
|
categories: (tenantId: string) => [...salesKeys.all, 'categories', tenantId] as const,
|
||
|
|
} as const;
|
||
|
|
|
||
|
|
// Queries
|
||
|
|
export const useSalesRecords = (
|
||
|
|
tenantId: string,
|
||
|
|
query?: SalesDataQuery,
|
||
|
|
options?: Omit<UseQueryOptions<SalesDataResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||
|
|
) => {
|
||
|
|
return useQuery<SalesDataResponse[], ApiError>({
|
||
|
|
queryKey: salesKeys.list(tenantId, query),
|
||
|
|
queryFn: () => salesService.getSalesRecords(tenantId, query),
|
||
|
|
enabled: !!tenantId,
|
||
|
|
staleTime: 30 * 1000, // 30 seconds
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useSalesRecord = (
|
||
|
|
tenantId: string,
|
||
|
|
recordId: string,
|
||
|
|
options?: Omit<UseQueryOptions<SalesDataResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||
|
|
) => {
|
||
|
|
return useQuery<SalesDataResponse, ApiError>({
|
||
|
|
queryKey: salesKeys.detail(tenantId, recordId),
|
||
|
|
queryFn: () => salesService.getSalesRecord(tenantId, recordId),
|
||
|
|
enabled: !!tenantId && !!recordId,
|
||
|
|
staleTime: 2 * 60 * 1000, // 2 minutes
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useSalesAnalytics = (
|
||
|
|
tenantId: string,
|
||
|
|
startDate?: string,
|
||
|
|
endDate?: string,
|
||
|
|
options?: Omit<UseQueryOptions<SalesAnalytics, ApiError>, 'queryKey' | 'queryFn'>
|
||
|
|
) => {
|
||
|
|
return useQuery<SalesAnalytics, ApiError>({
|
||
|
|
queryKey: salesKeys.analytics(tenantId, startDate, endDate),
|
||
|
|
queryFn: () => salesService.getSalesAnalytics(tenantId, startDate, endDate),
|
||
|
|
enabled: !!tenantId,
|
||
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useProductSales = (
|
||
|
|
tenantId: string,
|
||
|
|
inventoryProductId: string,
|
||
|
|
startDate?: string,
|
||
|
|
endDate?: string,
|
||
|
|
options?: Omit<UseQueryOptions<SalesDataResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||
|
|
) => {
|
||
|
|
return useQuery<SalesDataResponse[], ApiError>({
|
||
|
|
queryKey: salesKeys.productSales(tenantId, inventoryProductId, startDate, endDate),
|
||
|
|
queryFn: () => salesService.getProductSales(tenantId, inventoryProductId, startDate, endDate),
|
||
|
|
enabled: !!tenantId && !!inventoryProductId,
|
||
|
|
staleTime: 2 * 60 * 1000, // 2 minutes
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useProductCategories = (
|
||
|
|
tenantId: string,
|
||
|
|
options?: Omit<UseQueryOptions<string[], ApiError>, 'queryKey' | 'queryFn'>
|
||
|
|
) => {
|
||
|
|
return useQuery<string[], ApiError>({
|
||
|
|
queryKey: salesKeys.categories(tenantId),
|
||
|
|
queryFn: () => salesService.getProductCategories(tenantId),
|
||
|
|
enabled: !!tenantId,
|
||
|
|
staleTime: 10 * 60 * 1000, // 10 minutes
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
// Mutations
|
||
|
|
export const useCreateSalesRecord = (
|
||
|
|
options?: UseMutationOptions<SalesDataResponse, ApiError, { tenantId: string; salesData: SalesDataCreate }>
|
||
|
|
) => {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
|
||
|
|
return useMutation<SalesDataResponse, ApiError, { tenantId: string; salesData: SalesDataCreate }>({
|
||
|
|
mutationFn: ({ tenantId, salesData }) => salesService.createSalesRecord(tenantId, salesData),
|
||
|
|
onSuccess: (data, { tenantId }) => {
|
||
|
|
// Invalidate sales lists to refresh data
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.lists() });
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.analytics(tenantId) });
|
||
|
|
// Set the new record in cache
|
||
|
|
queryClient.setQueryData(salesKeys.detail(tenantId, data.id), data);
|
||
|
|
},
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useUpdateSalesRecord = (
|
||
|
|
options?: UseMutationOptions<
|
||
|
|
SalesDataResponse,
|
||
|
|
ApiError,
|
||
|
|
{ tenantId: string; recordId: string; updateData: SalesDataUpdate }
|
||
|
|
>
|
||
|
|
) => {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
|
||
|
|
return useMutation<
|
||
|
|
SalesDataResponse,
|
||
|
|
ApiError,
|
||
|
|
{ tenantId: string; recordId: string; updateData: SalesDataUpdate }
|
||
|
|
>({
|
||
|
|
mutationFn: ({ tenantId, recordId, updateData }) =>
|
||
|
|
salesService.updateSalesRecord(tenantId, recordId, updateData),
|
||
|
|
onSuccess: (data, { tenantId, recordId }) => {
|
||
|
|
// Update the record cache
|
||
|
|
queryClient.setQueryData(salesKeys.detail(tenantId, recordId), data);
|
||
|
|
// Invalidate related queries
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.lists() });
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.analytics(tenantId) });
|
||
|
|
},
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useDeleteSalesRecord = (
|
||
|
|
options?: UseMutationOptions<{ message: string }, ApiError, { tenantId: string; recordId: string }>
|
||
|
|
) => {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
|
||
|
|
return useMutation<{ message: string }, ApiError, { tenantId: string; recordId: string }>({
|
||
|
|
mutationFn: ({ tenantId, recordId }) => salesService.deleteSalesRecord(tenantId, recordId),
|
||
|
|
onSuccess: (data, { tenantId, recordId }) => {
|
||
|
|
// Remove from cache
|
||
|
|
queryClient.removeQueries({ queryKey: salesKeys.detail(tenantId, recordId) });
|
||
|
|
// Invalidate related queries
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.lists() });
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.analytics(tenantId) });
|
||
|
|
},
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
export const useValidateSalesRecord = (
|
||
|
|
options?: UseMutationOptions<
|
||
|
|
SalesDataResponse,
|
||
|
|
ApiError,
|
||
|
|
{ tenantId: string; recordId: string; validationNotes?: string }
|
||
|
|
>
|
||
|
|
) => {
|
||
|
|
const queryClient = useQueryClient();
|
||
|
|
|
||
|
|
return useMutation<
|
||
|
|
SalesDataResponse,
|
||
|
|
ApiError,
|
||
|
|
{ tenantId: string; recordId: string; validationNotes?: string }
|
||
|
|
>({
|
||
|
|
mutationFn: ({ tenantId, recordId, validationNotes }) =>
|
||
|
|
salesService.validateSalesRecord(tenantId, recordId, validationNotes),
|
||
|
|
onSuccess: (data, { tenantId, recordId }) => {
|
||
|
|
// Update the record cache
|
||
|
|
queryClient.setQueryData(salesKeys.detail(tenantId, recordId), data);
|
||
|
|
// Invalidate sales lists to reflect validation status
|
||
|
|
queryClient.invalidateQueries({ queryKey: salesKeys.lists() });
|
||
|
|
},
|
||
|
|
...options,
|
||
|
|
});
|
||
|
|
};
|